[nginx] Core: fixed segfault with too large bucket sizes (ticket #1806).

Maxim Dounin mdounin at mdounin.ru
Thu Jul 18 15:28:35 UTC 2019


details:   https://hg.nginx.org/nginx/rev/c3f60d618c17
branches:  
changeset: 7536:c3f60d618c17
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Thu Jul 18 18:27:44 2019 +0300
description:
Core: fixed segfault with too large bucket sizes (ticket #1806).

To save memory hash code uses u_short to store resulting bucket sizes,
so maximum bucket size is limited to 65536 minus ngx_cacheline_size (larger
values will be aligned to 65536 which will overflow u_short).  However,
there were no checks to enforce this, and using larger bucket sizes
resulted in overflows and segmentation faults.

Appropriate safety checks to enforce this added to ngx_hash_init().

diffstat:

 src/core/ngx_hash.c |  30 +++++++++++++++++++++++++-----
 1 files changed, 25 insertions(+), 5 deletions(-)

diffs (61 lines):

diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -265,6 +265,14 @@ ngx_hash_init(ngx_hash_init_t *hinit, ng
         return NGX_ERROR;
     }
 
+    if (hinit->bucket_size > 65536 - ngx_cacheline_size) {
+        ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+                      "could not build %s, too large "
+                      "%s_bucket_size: %i",
+                      hinit->name, hinit->name, hinit->bucket_size);
+        return NGX_ERROR;
+    }
+
     for (n = 0; n < nelts; n++) {
         if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
         {
@@ -300,17 +308,19 @@ ngx_hash_init(ngx_hash_init_t *hinit, ng
             }
 
             key = names[n].key_hash % size;
-            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+            len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);
 
 #if 0
             ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
-                          "%ui: %ui %ui \"%V\"",
-                          size, key, test[key], &names[n].key);
+                          "%ui: %ui %uz \"%V\"",
+                          size, key, len, &names[n].key);
 #endif
 
-            if (test[key] > (u_short) bucket_size) {
+            if (len > bucket_size) {
                 goto next;
             }
+
+            test[key] = (u_short) len;
         }
 
         goto found;
@@ -341,7 +351,17 @@ found:
         }
 
         key = names[n].key_hash % size;
-        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+        len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);
+
+        if (len > 65536 - ngx_cacheline_size) {
+            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+                          "could not build %s, you should "
+                          "increase %s_max_size: %i",
+                          hinit->name, hinit->name, hinit->max_size);
+            return NGX_ERROR;
+        }
+
+        test[key] = (u_short) len;
     }
 
     len = 0;


More information about the nginx-devel mailing list