[hackers] [quark] Properly set resource limits || Laslo Hunhold

From: <git_AT_suckless.org>
Date: Thu, 21 Jan 2021 01:11:07 +0100 (CET)

commit f3b6d5efc375bedd287897dcaabffec7f9222ea6
Author: Laslo Hunhold <dev_AT_frign.de>
AuthorDate: Thu Jan 21 00:58:01 2021 +0100
Commit: Laslo Hunhold <dev_AT_frign.de>
CommitDate: Thu Jan 21 01:10:52 2021 +0100

    Properly set resource limits
    
    Quark sets two rlimits, the maximum number of open file descriptors and
    the maximum number of threads. I made a mistake in the calculation of
    the former (forgetting about slots) and gave it a bit more headroom so
    we don't run into problems. Given the number open file descriptors is
    per-process, this can be considered done.
    
    The thread-limit is a different matter, because it's per user. To work
    around this, the program tries increasing the per-user-limit by the
    number of threads needed. If this hits the upper bound imposed by the
    system, we ignore this though, and just carry on. Sadly the getrlimit()
    errnos are not as insightful as I'd wish, because it uses EPERM for a
    lot of things other than excessive limits, but this is a good
    compromise.
    If we fail because of CAP_SYS_RESOURCE, which might remain uncaught
    previously in case setrlimit on RLIMIT_NOFILE lowers both limits, it
    will remain unreported here. However, I wouldn't want to spam the command
    line with such an error message when it's only a very high preset limit
    by a user triggering a kernel-limit-overflow.
    In case it is a lack of CAP_SYS_RESOURCE, this is later reported when
    pthread_create() fails, so we cover this case as well and thus give
    the user proper feedback on what he can do.
    
    Signed-off-by: Laslo Hunhold <dev_AT_frign.de>

diff --git a/main.c b/main.c
index 86b2d0c..e383498 100644
--- a/main.c
+++ b/main.c
_AT_@ -347,7 +347,14 @@ handle_connections(int *insock, size_t nthreads, size_t nslots,
         }
         for (i = 0; i < nthreads; i++) {
                 if (pthread_create(&thread[i], NULL, thread_method, &d[i]) != 0) {
- die("pthread_create:");
+ if (errno == EAGAIN) {
+ die("You need to run as root or have "
+ "CAP_SYS_RESOURCE set, or are trying "
+ "to create more threads than the "
+ "system can offer");
+ } else {
+ die("pthread_create:");
+ }
                 }
         }
 
_AT_@ -603,12 +610,20 @@ main(int argc, char *argv[])
 
         handlesignals(sigcleanup);
 
- /* set the fd-limit (3 initial + 4 per thread) */
- rlim.rlim_cur = rlim.rlim_max = 3 + 4 * (2 + nthreads);
+ /*
+ * set the maximum number of open file descriptors as needed
+ * - 3 initial fd's
+ * - nthreads fd's for the listening socket
+ * - (nthreads * nslots) fd's for the connection-fd
+ * - (5 * nthreads) fd's for general purpose thread-use
+ */
+ rlim.rlim_cur = rlim.rlim_max = 3 + nthreads + nthreads * nslots +
+ 5 * nthreads;
         if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
                 if (errno == EPERM) {
                         die("You need to run as root or have "
- "CAP_SYS_RESOURCE set");
+ "CAP_SYS_RESOURCE set, or are asking for more "
+ "file descriptors than the system can offer");
                 } else {
                         die("setrlimit:");
                 }
_AT_@ -645,15 +660,32 @@ main(int argc, char *argv[])
                         die("signal: Failed to set SIG_IGN on SIGPIPE");
                 }
 
- /* set the thread limit (2 + nthreads) */
- rlim.rlim_cur = rlim.rlim_max = 2 + nthreads;
- if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
- if (errno == EPERM) {
- die("You need to run as root or have "
- "CAP_SYS_RESOURCE set");
- } else {
- die("setrlimit:");
+ /*
+ * try increasing the thread-limit by the number
+ * of threads we need (which is the only reliable
+ * workaround I know given the thread-limit is per user
+ * rather than per process), but ignore EPERM errors,
+ * because this most probably means the user has already
+ * set the value to the kernel's limit, and there's not
+ * much we can do in any other case.
+ * There's also no danger of overflow as the value
+ * returned by getrlimit() is way below the limits of the
+ * rlim_t datatype.
+ */
+ if (getrlimit(RLIMIT_NPROC, &rlim) < 0) {
+ die("getrlimit:");
+ }
+ if (rlim.rlim_max == RLIM_INFINITY) {
+ if (rlim.rlim_cur != RLIM_INFINITY) {
+ /* try increasing current limit by nthreads */
+ rlim.rlim_cur += nthreads;
                         }
+ } else {
+ /* try increasing current and hard limit by nthreads */
+ rlim.rlim_cur = rlim.rlim_max += nthreads;
+ }
+ if (setrlimit(RLIMIT_NPROC, &rlim) < 0 && errno != EPERM) {
+ die("setrlimit()");
                 }
 
                 /* limit ourselves to reading the servedir and block further unveils */
Received on Thu Jan 21 2021 - 01:11:07 CET

This archive was generated by hypermail 2.3.0 : Thu Jan 21 2021 - 01:12:35 CET