[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