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.30.4~4 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/%7B@url%7D?a=commitdiff_plain;h=326b17b0038321c29252e79bf4c53d4773fcb8b5;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 f7f0696a8..21c106778 100644 --- a/src/http/modules/ngx_http_fastcgi_module.c +++ b/src/http/modules/ngx_http_fastcgi_module.c @@ -845,8 +845,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; @@ -861,6 +861,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; @@ -900,8 +901,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) { @@ -1057,6 +1060,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; @@ -1089,6 +1093,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) { @@ -1107,12 +1117,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 1895ef31b..681828109 100644 --- a/src/http/modules/ngx_http_grpc_module.c +++ b/src/http/modules/ngx_http_grpc_module.c @@ -717,8 +717,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; @@ -742,6 +744,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) { @@ -838,8 +842,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; @@ -850,6 +854,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; @@ -1046,6 +1052,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; @@ -1070,16 +1078,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; @@ -1087,6 +1117,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 276cc0750..f3263cd5f 100644 --- a/src/http/modules/ngx_http_proxy_module.c +++ b/src/http/modules/ngx_http_proxy_module.c @@ -1173,7 +1173,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; @@ -1227,6 +1227,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; @@ -1265,7 +1267,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; @@ -1301,9 +1302,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; @@ -1395,6 +1398,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; @@ -1427,6 +1431,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) { @@ -1435,6 +1447,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; } @@ -1485,6 +1505,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) { @@ -1492,6 +1513,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 010c02a86..ae6007298 100644 --- a/src/http/modules/ngx_http_proxy_v2_module.c +++ b/src/http/modules/ngx_http_proxy_v2_module.c @@ -317,8 +317,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; @@ -370,6 +372,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) @@ -513,8 +517,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; @@ -525,6 +529,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; @@ -727,6 +733,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; @@ -751,16 +759,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; @@ -768,6 +798,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) @@ -935,6 +979,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; @@ -944,6 +989,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 b4a73a5a6..7276b4a6f 100644 --- a/src/http/modules/ngx_http_scgi_module.c +++ b/src/http/modules/ngx_http_scgi_module.c @@ -643,7 +643,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; @@ -668,6 +668,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; @@ -705,8 +706,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) { @@ -821,6 +824,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; @@ -859,6 +863,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 @@ -866,6 +874,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); @@ -873,6 +890,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 ecd6dd0d7..71a658c30 100644 --- a/src/http/modules/ngx_http_uwsgi_module.c +++ b/src/http/modules/ngx_http_uwsgi_module.c @@ -866,7 +866,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; @@ -879,6 +879,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; @@ -916,8 +917,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) { @@ -1049,6 +1052,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; @@ -1081,12 +1085,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); @@ -1095,6 +1111,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, @@ -1103,6 +1123,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; }