From: Maxim Dounin Date: Fri, 19 Jun 2026 19:54:46 +0000 (+0300) Subject: Script: buffer overrun protection in direct script usage. X-Git-Tag: release-1.31.3~4 X-Git-Url: http://git.kaiwu.me/http/$%7BGITURL%7D/doc/static/gitweb.js?a=commitdiff_plain;h=25f920eca977139dc6fc7638b419169a602a0064;p=nginx.git 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. This change is a modified version of a patch by Maxim Dounin. The modifications include ngx_http_proxy_v2_module and hardened size checks. Signed-off-by: Roman Arutyunyan Origin: --- diff --git a/src/http/modules/ngx_http_fastcgi_module.c b/src/http/modules/ngx_http_fastcgi_module.c index cb34dcc8d..96e0b3b5d 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -859,8 +859,8 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) { 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; @@ -875,6 +875,7 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) ngx_http_script_len_code_pt lcode; len = 0; + params_len = 0; header_params = 0; ignored = NULL; @@ -914,8 +915,10 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) 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) { @@ -1071,6 +1074,7 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) e.ip = params->values->elts; e.pos = b->last; + e.end = b->last + params_len; e.request = r; e.flushed = 1; @@ -1103,6 +1107,12 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) 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) { @@ -1121,12 +1131,22 @@ ngx_http_fastcgi_create_request(ngx_http_request_t *r) } 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), val_len, e.pos - val_len); } + if (e.pos != e.end) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "fastcgi request length mismatch"); + return NGX_ERROR; + } + b->last = e.pos; } diff --git a/src/http/modules/ngx_http_grpc_module.c b/src/http/modules/ngx_http_grpc_module.c index 533806ced..c19833fbc 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -733,8 +733,10 @@ ngx_http_grpc_eval(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx, 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; @@ -758,6 +760,8 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) 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) { @@ -854,8 +858,8 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) return NGX_ERROR; } - 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; @@ -866,6 +870,8 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) } } + len += headers_len; + if (glcf->upstream.pass_request_headers) { part = &r->headers_in.headers.part; header = part->elts; @@ -1062,6 +1068,8 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) 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; @@ -1086,16 +1094,38 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) continue; } + if (headers_end - b->last < 1) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no buffer space in grpc create request"); + return NGX_ERROR; + } + *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; + } + + key_len = e.pos - key_tmp; + + if (headers_end - b->last + < (ssize_t) (NGX_HTTP_V2_INT_OCTETS + 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; @@ -1103,6 +1133,20 @@ ngx_http_grpc_create_request(ngx_http_request_t *r) } e.ip += sizeof(uintptr_t); + if (e.status) { + return NGX_ERROR; + } + + val_len = e.pos - val_tmp; + + if (headers_end - b->last + < (ssize_t) (NGX_HTTP_V2_INT_OCTETS + 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 index 18e7049c5..149b44725 100644 --- a/src/http/modules/ngx_http_index_module.c +++ b/src/http/modules/ngx_http_index_module.c @@ -126,6 +126,7 @@ ngx_http_index_handler(ngx_http_request_t *r) name = NULL; /* suppress MSVC warning */ path.data = NULL; + e.status = 0; index = ilcf->indices->elts; for (i = 0; i < ilcf->indices->nelts; i++) { @@ -180,18 +181,28 @@ ngx_http_index_handler(ngx_http_request_t *r) } 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 index 8a9bffe10..3ae8c9766 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1189,7 +1189,7 @@ ngx_http_proxy_create_key(ngx_http_request_t *r) static ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r) { - 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; @@ -1243,6 +1243,8 @@ ngx_http_proxy_create_request(ngx_http_request_t *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; @@ -1281,7 +1283,6 @@ ngx_http_proxy_create_request(ngx_http_request_t *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; @@ -1317,9 +1318,11 @@ ngx_http_proxy_create_request(ngx_http_request_t *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) { part = &r->headers_in.headers.part; @@ -1411,6 +1414,7 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) e.ip = headers->values->elts; e.pos = b->last; + e.end = b->last + headers_len; e.request = r; e.flushed = 1; @@ -1443,6 +1447,14 @@ ngx_http_proxy_create_request(ngx_http_request_t *r) 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++ = ':'; *e.pos++ = ' '; while (*(uintptr_t *) e.ip) { @@ -1451,6 +1463,14 @@ ngx_http_proxy_create_request(ngx_http_request_t *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; } @@ -1501,6 +1521,7 @@ ngx_http_proxy_create_request(ngx_http_request_t *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) { @@ -1508,6 +1529,10 @@ ngx_http_proxy_create_request(ngx_http_request_t *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_proxy_v2_module.c b/src/http/modules/ngx_http_proxy_v2_module.c index cecc56ba8..372ab05e9 100644 --- a/src/http/modules/ngx_http_proxy_v2_module.c +++ b/src/http/modules/ngx_http_proxy_v2_module.c @@ -319,8 +319,10 @@ ngx_http_proxy_v2_handler(ngx_http_request_t *r) static ngx_int_t ngx_http_proxy_v2_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, loc_len, body_len; uintptr_t escape; ngx_buf_t *b; @@ -372,6 +374,8 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) len = sizeof(ngx_http_proxy_v2_connection_start) - 1 + sizeof(ngx_http_proxy_v2_frame_t); /* headers frame */ + headers_len = 0; + /* :method header */ if ((method.len == 3 && ngx_strncmp(method.data, "GET", 3) == 0) @@ -515,8 +519,8 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) return NGX_ERROR; } - 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; @@ -527,6 +531,8 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) } } + len += headers_len; + if (plcf->upstream.pass_request_headers) { part = &r->headers_in.headers.part; header = part->elts; @@ -729,6 +735,8 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) le.ip = headers->lengths->elts; + headers_end = b->last + headers_len; + while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; @@ -753,16 +761,38 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) continue; } + if (headers_end - b->last < 1) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no buffer space in HTTP/2 create request"); + return NGX_ERROR; + } + *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; + } + + key_len = e.pos - key_tmp; + + if (headers_end - b->last + < (ssize_t) (NGX_HTTP_V2_INT_OCTETS + key_len)) + { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no buffer space in HTTP/2 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; @@ -770,6 +800,20 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) } e.ip += sizeof(uintptr_t); + if (e.status) { + return NGX_ERROR; + } + + val_len = e.pos - val_tmp; + + if (headers_end - b->last + < (ssize_t) (NGX_HTTP_V2_INT_OCTETS + val_len)) + { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "no buffer space in HTTP/2 create request"); + return NGX_ERROR; + } + b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp); #if (NGX_DEBUG) @@ -937,6 +981,7 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) e.ip = plcf->body_values->elts; e.pos = b->last; + e.end = b->last + body_len; e.request = r; e.flushed = 1; e.skip = 0; @@ -946,6 +991,10 @@ ngx_http_proxy_v2_create_request(ngx_http_request_t *r) code((ngx_http_script_engine_t *) &e); } + if (e.status) { + return NGX_ERROR; + } + b->last = e.pos; b->last_buf = 1; diff --git a/src/http/modules/ngx_http_scgi_module.c b/src/http/modules/ngx_http_scgi_module.c index 05cbdded0..fd0907c13 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -657,7 +657,7 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) { 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; @@ -682,6 +682,7 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) len = sizeof("CONTENT_LENGTH") + content_length.len + 1; + params_len = 0; header_params = 0; ignored = NULL; @@ -719,8 +720,10 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) continue; } - len += key_len + val_len + 1; + params_len += key_len + val_len + 1; } + + len += params_len; } if (scf->upstream.pass_request_headers) { @@ -835,6 +838,7 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) e.ip = params->values->elts; e.pos = b->last; + e.end = b->last + params_len; e.request = r; e.flushed = 1; @@ -873,6 +877,10 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) 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 @@ -880,6 +888,15 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) 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); @@ -887,6 +904,12 @@ ngx_http_scgi_create_request(ngx_http_request_t *r) "scgi param: \"%s: %s\"", key, val); } + if (e.pos != e.end) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "scgi request length mismatch"); + return NGX_ERROR; + } + b->last = e.pos; } diff --git a/src/http/modules/ngx_http_try_files_module.c b/src/http/modules/ngx_http_try_files_module.c index ce783a24d..bbdab8598 100644 --- a/src/http/modules/ngx_http_try_files_module.c +++ b/src/http/modules/ngx_http_try_files_module.c @@ -78,7 +78,7 @@ ngx_module_t ngx_http_try_files_module = { static ngx_int_t ngx_http_try_files_handler(ngx_http_request_t *r) { - size_t len, root, alias, reserve, allocated; + size_t len, root, alias, reserve, allocated, n; u_char *p, *name; ngx_str_t path, args; ngx_uint_t test_dir; @@ -162,8 +162,15 @@ ngx_http_try_files_handler(ngx_http_request_t *r) path.len = (name + tf->name.len - 1) - path.data; } else { + n = allocated; + + if (alias != NGX_MAX_SIZE_T_VALUE) { + n += (r->uri.len - alias); + } + e.ip = tf->values->elts; e.pos = name; + e.end = name + n; e.flushed = 1; while (*(uintptr_t *) e.ip) { @@ -171,6 +178,14 @@ ngx_http_try_files_handler(ngx_http_request_t *r) 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 index f5841375f..8d243bc90 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -880,7 +880,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; @@ -893,6 +893,7 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) ngx_http_script_len_code_pt lcode; len = 0; + params_len = 0; header_params = 0; ignored = NULL; @@ -930,8 +931,10 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *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) { @@ -1063,6 +1066,7 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) e.ip = params->values->elts; e.pos = b->last; + e.end = b->last + params_len; e.request = r; e.flushed = 1; @@ -1095,12 +1099,24 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *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); @@ -1109,6 +1125,10 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *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, @@ -1117,6 +1137,12 @@ ngx_http_uwsgi_create_request(ngx_http_request_t *r) val_len, e.pos - val_len); } + if (e.pos != e.end) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, + "uwsgi request length mismatch"); + return NGX_ERROR; + } + b->last = e.pos; }