[PATCH 3 of 3] Script: buffer overrun protection in direct script usage

Maxim Dounin mdounin at mdounin.ru
Mon Jun 8 17:37:23 UTC 2026


# HG changeset patch
# User Maxim Dounin <mdounin at mdounin.ru>
# Date 1780936500 -10800
#      Mon Jun 08 19:35:00 2026 +0300
# Node ID 67059a7429f113a0af1e0b7d21ff8bf7ef4ac9f7
# Parent  1794ecca0620d0e54f5649c3405ea010e8abce2e
Script: buffer overrun protection in direct script usage.

Following the previous change, this change adds script overrun
protection to direct script evaluation in the proxy, fastcgi, scgi,
uwsgi, grpc proxy, index, and try_files modules.

diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c
+++ b/src/http/modules/ngx_http_fastcgi_module.c
@@ -836,8 +836,8 @@ ngx_http_fastcgi_create_request(ngx_http
 {
     off_t                         file_pos;
     u_char                        ch, sep, *pos, *lowcase_key;
-    size_t                        size, len, key_len, val_len, padding,
-                                  allocated;
+    size_t                        size, len, params_len,
+                                  key_len, val_len, padding, allocated;
     ngx_uint_t                    i, n, next, hash, skip_empty, header_params;
     ngx_buf_t                    *b;
     ngx_chain_t                  *cl, *body;
@@ -852,6 +852,7 @@ ngx_http_fastcgi_create_request(ngx_http
     ngx_http_script_len_code_pt   lcode;
 
     len = 0;
+    params_len = 0;
     header_params = 0;
     ignored = NULL;
 
@@ -891,8 +892,10 @@ ngx_http_fastcgi_create_request(ngx_http
                 continue;
             }
 
-            len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
+            params_len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;
         }
+
+        len += params_len;
     }
 
     if (flcf->upstream.pass_request_headers) {
@@ -1048,6 +1051,7 @@ ngx_http_fastcgi_create_request(ngx_http
 
         e.ip = params->values->elts;
         e.pos = b->last;
+        e.end = b->last + params_len;
         e.request = r;
         e.flushed = 1;
 
@@ -1080,6 +1084,12 @@ ngx_http_fastcgi_create_request(ngx_http
                 continue;
             }
 
+            if (ngx_http_script_check_length(&e, 1 + ((val_len > 127) ? 4 : 1))
+                != NGX_OK)
+            {
+                return NGX_ERROR;
+            }
+
             *e.pos++ = (u_char) key_len;
 
             if (val_len > 127) {
@@ -1098,6 +1108,10 @@ ngx_http_fastcgi_create_request(ngx_http
             }
             e.ip += sizeof(uintptr_t);
 
+            if (e.status) {
+                return NGX_ERROR;
+            }
+
             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                            "fastcgi param: \"%*s: %*s\"",
                            key_len, e.pos - (key_len + val_len),
diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c
--- a/src/http/modules/ngx_http_grpc_module.c
+++ b/src/http/modules/ngx_http_grpc_module.c
@@ -710,8 +710,10 @@ ngx_http_grpc_eval(ngx_http_request_t *r
 static ngx_int_t
 ngx_http_grpc_create_request(ngx_http_request_t *r)
 {
-    u_char                       *p, *tmp, *key_tmp, *val_tmp, *headers_frame;
-    size_t                        len, tmp_len, key_len, val_len, uri_len;
+    u_char                       *p, *tmp, *key_tmp, *val_tmp, *headers_frame,
+                                 *headers_end;
+    size_t                        len, headers_len, tmp_len,
+                                  key_len, val_len, uri_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
     ngx_uint_t                    i, next;
@@ -735,6 +737,8 @@ ngx_http_grpc_create_request(ngx_http_re
     len = sizeof(ngx_http_grpc_connection_start) - 1
           + sizeof(ngx_http_grpc_frame_t);             /* headers frame */
 
+    headers_len = 0;
+
     /* :method header */
 
     if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) {
@@ -801,8 +805,8 @@ ngx_http_grpc_create_request(ngx_http_re
             continue;
         }
 
-        len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
-                 + NGX_HTTP_V2_INT_OCTETS + val_len;
+        headers_len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len
+                         + NGX_HTTP_V2_INT_OCTETS + val_len;
 
         if (tmp_len < key_len) {
             tmp_len = key_len;
@@ -813,6 +817,8 @@ ngx_http_grpc_create_request(ngx_http_re
         }
     }
 
+    len += headers_len;
+
     if (glcf->upstream.pass_request_headers) {
         part = &r->headers_in.headers.part;
         header = part->elts;
@@ -995,6 +1001,8 @@ ngx_http_grpc_create_request(ngx_http_re
 
     le.ip = glcf->headers.lengths->elts;
 
+    headers_end = b->last + headers_len;
+
     while (*(uintptr_t *) le.ip) {
 
         lcode = *(ngx_http_script_len_code_pt *) le.ip;
@@ -1022,13 +1030,25 @@ ngx_http_grpc_create_request(ngx_http_re
         *b->last++ = 0;
 
         e.pos = key_tmp;
+        e.end = key_tmp + tmp_len;
 
         code = *(ngx_http_script_code_pt *) e.ip;
         code((ngx_http_script_engine_t *) &e);
 
+        if (e.status) {
+            return NGX_ERROR;
+        }
+
+        if (headers_end - b->last < (ssize_t) key_len) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "no buffer space in grpc create request");
+            return NGX_ERROR;
+        }
+
         b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp);
 
         e.pos = val_tmp;
+        e.end = val_tmp + tmp_len;
 
         while (*(uintptr_t *) e.ip) {
             code = *(ngx_http_script_code_pt *) e.ip;
@@ -1036,6 +1056,16 @@ ngx_http_grpc_create_request(ngx_http_re
         }
         e.ip += sizeof(uintptr_t);
 
+        if (e.status) {
+            return NGX_ERROR;
+        }
+
+        if (headers_end - b->last < (ssize_t) val_len) {
+            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+                          "no buffer space in grpc create request");
+            return NGX_ERROR;
+        }
+
         b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp);
 
 #if (NGX_DEBUG)
diff --git a/src/http/modules/ngx_http_index_module.c b/src/http/modules/ngx_http_index_module.c
--- a/src/http/modules/ngx_http_index_module.c
+++ b/src/http/modules/ngx_http_index_module.c
@@ -130,6 +130,7 @@ ngx_http_index_handler(ngx_http_request_
     name = NULL;
     /* suppress MSVC warning */
     path.data = NULL;
+    e.status = 0;
 
     index = ilcf->indices->elts;
     for (i = 0; i < ilcf->indices->nelts; i++) {
@@ -184,18 +185,28 @@ ngx_http_index_handler(ngx_http_request_
         } else {
             e.ip = index[i].values->elts;
             e.pos = name;
+            e.end = name + allocated;
 
             while (*(uintptr_t *) e.ip) {
                 code = *(ngx_http_script_code_pt *) e.ip;
                 code((ngx_http_script_engine_t *) &e);
             }
 
+            if (e.status) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            if (ngx_http_script_check_length(&e, 1) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
             if (*name == '/') {
-                uri.len = len - 1;
+                uri.len = e.pos - name;
                 uri.data = name;
                 return ngx_http_internal_redirect(r, &uri, &r->args);
             }
 
+            len = e.pos - name + 1;
             path.len = e.pos - path.data;
 
             *e.pos = '\0';
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -1253,7 +1253,7 @@ static ngx_int_t
 ngx_http_proxy_create_request(ngx_http_request_t *r)
 {
     u_char                       *key;
-    size_t                        len, uri_len, loc_len, body_len,
+    size_t                        len, uri_len, loc_len, body_len, headers_len,
                                   key_len, val_len;
     uintptr_t                     escape;
     ngx_buf_t                    *b;
@@ -1307,6 +1307,8 @@ ngx_http_proxy_create_request(ngx_http_r
     escape = 0;
     loc_len = 0;
     unparsed_uri = 0;
+    body_len = 0;
+    headers_len = 0;
 
     if (plcf->proxy_lengths && ctx->vars.uri.len) {
         uri_len = ctx->vars.uri.len;
@@ -1345,7 +1347,6 @@ ngx_http_proxy_create_request(ngx_http_r
         le.ip = plcf->body_lengths->elts;
         le.request = r;
         le.flushed = 1;
-        body_len = 0;
 
         while (*(uintptr_t *) le.ip) {
             lcode = *(ngx_http_script_len_code_pt *) le.ip;
@@ -1384,8 +1385,10 @@ ngx_http_proxy_create_request(ngx_http_r
             continue;
         }
 
-        len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1;
-    }
+        headers_len += key_len + sizeof(": ") - 1 + val_len + sizeof(CRLF) - 1;
+    }
+
+    len += headers_len;
 
 
     if (plcf->upstream.pass_request_headers) {
@@ -1478,6 +1481,7 @@ ngx_http_proxy_create_request(ngx_http_r
 
     e.ip = headers->values->elts;
     e.pos = b->last;
+    e.end = b->last + headers_len;
     e.request = r;
     e.flushed = 1;
 
@@ -1518,6 +1522,14 @@ ngx_http_proxy_create_request(ngx_http_r
             ctx->upgrade = 1;
         }
 
+        if (e.status) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_http_script_check_length(&e, 2) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
         *e.pos++ = ':'; *e.pos++ = ' ';
 
         while (*(uintptr_t *) e.ip) {
@@ -1526,6 +1538,14 @@ ngx_http_proxy_create_request(ngx_http_r
         }
         e.ip += sizeof(uintptr_t);
 
+        if (e.status) {
+            return NGX_ERROR;
+        }
+
+        if (ngx_http_script_check_length(&e, 2) != NGX_OK) {
+            return NGX_ERROR;
+        }
+
         *e.pos++ = CR; *e.pos++ = LF;
     }
 
@@ -1576,6 +1596,7 @@ ngx_http_proxy_create_request(ngx_http_r
     if (plcf->body_values) {
         e.ip = plcf->body_values->elts;
         e.pos = b->last;
+        e.end = b->last + body_len;
         e.skip = 0;
 
         while (*(uintptr_t *) e.ip) {
@@ -1583,6 +1604,10 @@ ngx_http_proxy_create_request(ngx_http_r
             code((ngx_http_script_engine_t *) &e);
         }
 
+        if (e.status) {
+            return NGX_ERROR;
+        }
+
         b->last = e.pos;
     }
 
diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c
--- a/src/http/modules/ngx_http_scgi_module.c
+++ b/src/http/modules/ngx_http_scgi_module.c
@@ -634,7 +634,7 @@ ngx_http_scgi_create_request(ngx_http_re
 {
     off_t                         content_length_n;
     u_char                        ch, sep, *key, *val, *lowcase_key;
-    size_t                        len, key_len, val_len, allocated;
+    size_t                        len, params_len, key_len, val_len, allocated;
     ngx_buf_t                    *b;
     ngx_str_t                     content_length;
     ngx_uint_t                    i, n, hash, skip_empty, header_params;
@@ -659,6 +659,7 @@ ngx_http_scgi_create_request(ngx_http_re
 
     len = sizeof("CONTENT_LENGTH") + content_length.len + 1;
 
+    params_len = 0;
     header_params = 0;
     ignored = NULL;
 
@@ -696,8 +697,10 @@ ngx_http_scgi_create_request(ngx_http_re
                 continue;
             }
 
-            len += key_len + val_len + 1;
+            params_len += key_len + val_len + 1;
         }
+
+        len += params_len;
     }
 
     if (scf->upstream.pass_request_headers) {
@@ -812,6 +815,7 @@ ngx_http_scgi_create_request(ngx_http_re
 
         e.ip = params->values->elts;
         e.pos = b->last;
+        e.end = b->last + params_len;
         e.request = r;
         e.flushed = 1;
 
@@ -850,6 +854,10 @@ ngx_http_scgi_create_request(ngx_http_re
             code = *(ngx_http_script_code_pt *) e.ip;
             code((ngx_http_script_engine_t *) &e);
 
+            if (e.status) {
+                return NGX_ERROR;
+            }
+
 #if (NGX_DEBUG)
             val = e.pos;
 #endif
@@ -857,6 +865,15 @@ ngx_http_scgi_create_request(ngx_http_re
                 code = *(ngx_http_script_code_pt *) e.ip;
                 code((ngx_http_script_engine_t *) &e);
             }
+
+            if (e.status) {
+                return NGX_ERROR;
+            }
+
+            if (ngx_http_script_check_length(&e, 1) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
             *e.pos++ = '\0';
             e.ip += sizeof(uintptr_t);
 
diff --git a/src/http/modules/ngx_http_try_files_module.c b/src/http/modules/ngx_http_try_files_module.c
--- a/src/http/modules/ngx_http_try_files_module.c
+++ b/src/http/modules/ngx_http_try_files_module.c
@@ -165,6 +165,7 @@ ngx_http_try_files_handler(ngx_http_requ
         } else {
             e.ip = tf->values->elts;
             e.pos = name;
+            e.end = name + (r->uri.len - alias) + allocated;
             e.flushed = 1;
 
             while (*(uintptr_t *) e.ip) {
@@ -172,6 +173,14 @@ ngx_http_try_files_handler(ngx_http_requ
                 code((ngx_http_script_engine_t *) &e);
             }
 
+            if (e.status) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+
+            if (ngx_http_script_check_length(&e, 1) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
             path.len = e.pos - path.data;
 
             *e.pos = '\0';
diff --git a/src/http/modules/ngx_http_uwsgi_module.c b/src/http/modules/ngx_http_uwsgi_module.c
--- a/src/http/modules/ngx_http_uwsgi_module.c
+++ b/src/http/modules/ngx_http_uwsgi_module.c
@@ -849,7 +849,7 @@ static ngx_int_t
 ngx_http_uwsgi_create_request(ngx_http_request_t *r)
 {
     u_char                        ch, sep, *lowcase_key;
-    size_t                        key_len, val_len, len, allocated;
+    size_t                        key_len, val_len, len, params_len, allocated;
     ngx_uint_t                    i, n, hash, skip_empty, header_params;
     ngx_buf_t                    *b;
     ngx_chain_t                  *cl, *body;
@@ -862,6 +862,7 @@ ngx_http_uwsgi_create_request(ngx_http_r
     ngx_http_script_len_code_pt   lcode;
 
     len = 0;
+    params_len = 0;
     header_params = 0;
     ignored = NULL;
 
@@ -899,8 +900,10 @@ ngx_http_uwsgi_create_request(ngx_http_r
                 continue;
             }
 
-            len += 2 + key_len + 2 + val_len;
+            params_len += 2 + key_len + 2 + val_len;
         }
+
+        len += params_len;
     }
 
     if (uwcf->upstream.pass_request_headers) {
@@ -1032,6 +1035,7 @@ ngx_http_uwsgi_create_request(ngx_http_r
 
         e.ip = params->values->elts;
         e.pos = b->last;
+        e.end = b->last + params_len;
         e.request = r;
         e.flushed = 1;
 
@@ -1064,12 +1068,24 @@ ngx_http_uwsgi_create_request(ngx_http_r
                 continue;
             }
 
+            if (ngx_http_script_check_length(&e, 2) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
             *e.pos++ = (u_char) (key_len & 0xff);
             *e.pos++ = (u_char) ((key_len >> 8) & 0xff);
 
             code = *(ngx_http_script_code_pt *) e.ip;
             code((ngx_http_script_engine_t *) &e);
 
+            if (e.status) {
+                return NGX_ERROR;
+            }
+
+            if (ngx_http_script_check_length(&e, 2) != NGX_OK) {
+                return NGX_ERROR;
+            }
+
             *e.pos++ = (u_char) (val_len & 0xff);
             *e.pos++ = (u_char) ((val_len >> 8) & 0xff);
 
@@ -1078,6 +1094,10 @@ ngx_http_uwsgi_create_request(ngx_http_r
                 code((ngx_http_script_engine_t *) &e);
             }
 
+            if (e.status) {
+                return NGX_ERROR;
+            }
+
             e.ip += sizeof(uintptr_t);
 
             ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,



More information about the nginx-devel mailing list