From 90b844212d38fec80d0690d30396c462aa5655cd Mon Sep 17 00:00:00 2001 From: Matt Stancliff Date: Sat, 22 Mar 2014 19:29:28 -0400 Subject: [PATCH] Fix infinite loop on startup if ulimit too low MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fun fact: rlim_t is an unsigned long long on all platforms. Continually subtracting from a rlim_t makes it get smaller and smaller until it wraps, then you're up to 2^64-1. This was causing an infinite loop on Redis startup if your ulimit was extremely (almost comically) low. The case of (f > oldlimit) would never be met in a case like: f = 150 while (f > 20) f -= 128 Since f is unsigned, it can't go negative and would take on values of: Iteration 1: 150 - 128 => 22 Iteration 2: 22 - 128 => 18446744073709551510 Iterations 3-∞: ... To catch the wraparound, we use the previous value of f stored in limit.rlimit_cur. If we subtract from f and get a larger number than the value it had previously, we print an error and exit since we don't have enough file descriptors to help the user at this point. Thanks to @bs3g for the inspiration to fix this problem. Patches existed from @bs3g at antirez#1227, but I needed to repair a few other parts of Redis simultaneously, so I didn't get a chance to use them. --- src/redis.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/redis.c b/src/redis.c index 43314bd5d..695513755 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1519,6 +1519,17 @@ void adjustOpenFilesLimit(void) { limit.rlim_max = f; if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break; f -= REDIS_EVENTLOOP_FDSET_INCR; + if (f > limit.rlim_cur) { + /* Instead of getting smaller, f just got bigger. + * That means it wrapped around its unsigned floor + * and is now closer to 2^64. We can't help anymore. */ + redisLog(REDIS_WARNING,"Failed to set max file limit. " + "You requested maxclients of %d " + "but your 'ulimit -n' is set to %llu. " + "Please increase your 'ulimit -n' to at least %llu.", + server.maxclients, oldlimit, maxfiles); + exit(1); + } } if (f < oldlimit) f = oldlimit; if (f != maxfiles) {