From fabianofurtado at gmail.com Mon May 4 01:22:16 2026 From: fabianofurtado at gmail.com (Fabiano Furtado) Date: Sun, 3 May 2026 22:22:16 -0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hi! On Thu, Apr 30, 2026 at 12:34?AM Maxim Dounin wrote: > > Hello! > > On Tue, Apr 28, 2026 at 08:32:32PM -0300, Fabiano Furtado wrote: > > > On Mon, Apr 27, 2026 at 9:04?PM Maxim Dounin wrote: > > > > > > Hello! > > > > > > On Mon, Apr 27, 2026 at 09:22:59 AM -0300, Fabiano Furtado wrote: > > > > > > > On Sun, Apr 26, 2026 at 7:53?PM Maxim Dounin wrote: > > > > > ... > > > > Anyway, I'm not sure if it's possible, but I'd like to try developing > > > > this patch. Would it be okay if I tried to create this new patch? > > > > > > Yep, feel free to. > > > > > > Just in case, some hints can be found here: > > > https://freenginx.org/en/docs/contributing_changes.html > > > > This patch introduces the "index off;" directive for freenginx, but I > > also had to modify other parts of the code to ensure this new > > directive behaves correctly. > > Please make sure to read the above link. In particular, following > the coding style and using Mercurial is highly recommended, as it > makes it much easier to review the code. > > > When "off" is applied in the config and an HTTP access occurs, a > > message appears in the error_log: "directory index of ".../" is > > forbidden". In this context, I believe this message is not > > particularly relevant for the error_log. So, I added a flag to the > > core called "log_forbidden", which works similarly to "log_not_found". > > I believe this change is completely orthogonal and has nothing to > do with the "index off;". The message is perfectly identical with > both "index off;" and the "index .non_such_file;" workaround, so > "index off;" introduces nothing new here. Also, the message can > be suppressed with "location ~ /$ { return 403; }" if no indexing > is indeed desired. > > That is, if at all, this should be a separate patch, explaining > the change and reasons for it. > > > As a result, instead of modifying only "ngx_http_index_module.c", I > > ended up changing four files: > > * src/http/ngx_http_core_module.c > > * src/http/ngx_http_core_module.h > > * src/http/modules/ngx_http_access_module.c > > * src/http/modules/ngx_http_index_module.c > > > > I would appreciate it if the code could be reviewed. Any feedback or > > suggestions are welcome. > > > > Thank you in advance for the opportunity to submit this patch. > > Fabiano Furtado > > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.c freenginx-1.30.0_patched/src/http/ngx_http_core_module.c >ngx_http_core_module.c.patch > > --- freenginx-1.30.0/src/http/ngx_http_core_module.c 2026-04-14 05:23:27.000000000 -0300 > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.c 2026-04-28 19:56:18.478687257 -0300 > > @@ -628,7 +628,14 @@ > > offsetof(ngx_http_core_loc_conf_t, msie_refresh), > > NULL }, > > > > - { ngx_string("log_not_found"), > > + { ngx_string("log_forbidden"), > > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, > > + ngx_conf_set_flag_slot, > > + NGX_HTTP_LOC_CONF_OFFSET, > > + offsetof(ngx_http_core_loc_conf_t, log_forbidden), > > + NULL }, > > + > > + { ngx_string("log_not_found"), > > Note that "log_not_found" in diff suggests there is a white space > damage. And, indeed, the line lost one of the leading spaces. > > Also, in many cases it is a good idea to define new directives > after the one you want to mimic, and not before it. > > > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, > > ngx_conf_set_flag_slot, > > NGX_HTTP_LOC_CONF_OFFSET, > > @@ -1177,7 +1184,8 @@ > > ngx_http_core_post_access_phase(ngx_http_request_t *r, > > ngx_http_phase_handler_t *ph) > > { > > - ngx_int_t access_code; > > + ngx_int_t access_code; > > + ngx_http_core_loc_conf_t *clcf; > > > > ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, > > "post access phase: %ui", r->phase_handler); > > @@ -1187,7 +1195,9 @@ > > if (access_code) { > > r->access_code = 0; > > > > - if (access_code == NGX_HTTP_FORBIDDEN) { > > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > + > > + if ((clcf->log_forbidden) && (access_code == NGX_HTTP_FORBIDDEN)) { > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > "access forbidden by rule"); > > } > > Requests rejected by the access phase handlers are generally > expected to be logged. Also, there are various other cases when > access phase handlers log errors, such as in the > ngx_http_auth_basic_module on user/password mismatch. > > While changing this is possible and I can't say I strongly object > it, I would like to ensure that this is done in some consistent > manner, affecting all the standard access modules, and not just > the errors typically returned by the access module. > > Note well that there are various other places where > NGX_HTTP_FORBIDDEN can be returned, and these log errors, > including with your patch - autoindex, random index, flv, mp4, and > static modules all log errors and return NGX_HTTP_FORBIDDEN when > the file/directory cannot be opened due to OS-level access > restrictions, and your patch only changes relevant code in the > index module. It is not immediately clear how these are different > if the "log_forbidden" directive is expected to behave like > "log_not_found". And, at the same time, it might be seen as a > configuration error when a file which cannot be opened by > [free]nginx is accessible under the document root, so not logging > the error might be the wrong way to go. > > Also, it might be a better idea to follow the log limiting > approach as used by the limit_req and limit_conn modules, that is, > introduce some directives similar to limit_req_log_level. > > Not well that there is a couple of style issues in the code > suggested: it should be faster to check "access_code == > NGX_HTTP_FORBIDDEN" first, similarly to how clcf->log_not_found is > checked in the static module, and there is no need to place extra > parentheses where execution order is well defined and immediately > obvious. > > > @@ -1273,9 +1283,10 @@ > > ngx_http_core_content_phase(ngx_http_request_t *r, > > ngx_http_phase_handler_t *ph) > > { > > - size_t root; > > - ngx_int_t rc; > > - ngx_str_t path; > > + size_t root; > > + ngx_int_t rc; > > + ngx_str_t path; > > + ngx_http_core_loc_conf_t *clcf; > > > > if (r->content_handler) { > > r->write_event_handler = ngx_http_request_empty_handler; > > @@ -1306,7 +1317,11 @@ > > > > if (r->uri.data[r->uri.len - 1] == '/') { > > > > - if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { > > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > + > > + if ((clcf->log_forbidden) > > + && (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL)) { > > + > > Style issues here: unneeded parentheses, wrong indentation of the > second line, "{" after multiline conditions should be on its own > line. > > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > "directory index of \"%s\" is forbidden", path.data); > > } > > @@ -3649,6 +3664,7 @@ > > clcf->port_in_redirect = NGX_CONF_UNSET; > > clcf->msie_padding = NGX_CONF_UNSET; > > clcf->msie_refresh = NGX_CONF_UNSET; > > + clcf->log_forbidden = NGX_CONF_UNSET; > > clcf->log_not_found = NGX_CONF_UNSET; > > clcf->log_subrequest = NGX_CONF_UNSET; > > clcf->recursive_error_pages = NGX_CONF_UNSET; > > @@ -3923,6 +3939,7 @@ > > ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1); > > ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); > > ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0); > > + ngx_conf_merge_value(conf->log_forbidden, prev->log_forbidden, 1); > > ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); > > ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0); > > ngx_conf_merge_value(conf->recursive_error_pages, > > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_index_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c >ngx_http_index_module.c.patch > > --- freenginx-1.30.0/src/http/modules/ngx_http_index_module.c 2026-04-14 05:23:27.000000000 -0300 > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c 2026-04-28 20:07:00.825547974 -0300 > > @@ -20,6 +20,7 @@ > > typedef struct { > > ngx_array_t *indices; /* array of ngx_http_index_t */ > > size_t max_index_len; > > + ngx_flag_t off; > > } ngx_http_index_loc_conf_t; > > > > > > @@ -109,6 +110,12 @@ > > ngx_http_index_loc_conf_t *ilcf; > > ngx_http_script_len_code_pt lcode; > > > > + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > + > > + if (ilcf->off) { > > + return NGX_DECLINED; > > + } > > + > > if (r->uri.data[r->uri.len - 1] != '/') { > > return NGX_DECLINED; > > } > > I believe checking r->uri before and method before even trying to > access the configuration is optimal from performance point of > view, and this is what index module does now, and other modules do > as well (see autoindex or random index for some examples). There > is no need to change this order. > > > @@ -117,7 +124,6 @@ > > return NGX_DECLINED; > > } > > > > - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > > allocated = 0; > > @@ -369,8 +375,10 @@ > > u_char *file, ngx_err_t err) > > { > > if (err == NGX_EACCES) { > > - ngx_log_error(NGX_LOG_ERR, r->connection->log, err, > > - "\"%s\" is forbidden", file); > > + if (clcf->log_forbidden) { > > + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, > > + "\"%s\" is forbidden", file); > > + } > > > > return NGX_HTTP_FORBIDDEN; > > } > > Note that this change should be in a separate patch, and see above > for more comments about this. > > > @@ -396,6 +404,7 @@ > > > > conf->indices = NULL; > > conf->max_index_len = 0; > > + conf->off = NGX_CONF_UNSET; > > > > return conf; > > } > > @@ -409,6 +418,12 @@ > > > > ngx_http_index_t *index; > > > > + ngx_conf_merge_value(conf->off, prev->off, 0); > > + > > + if (conf->off) { > > + return NGX_CONF_OK; > > + } > > + > > if (conf->indices == NULL) { > > conf->indices = prev->indices; > > conf->max_index_len = prev->max_index_len; > > Consider the following configuration: > > server { > index off; > > location /public/ { > index index.html; > } > } > > With the merge logic you've implemented this will result in > "conf->off" being set in "location /public/", leading to no index > files being used there, which is obviously not the requested > behaviour. > > There are multiple possible solutions here: > > - write a custom merge logic, checking both conf->off and > conf->indices and inheriting conf->off and conf->indices only if > neither of the two was set at the current level, > > - change the code to instead rely on conf->indices special values > (for example, conf->indices == NGX_CONF_UNSET_PTR for not set, > and NULL for off; or indices->nelts == 0 for off), > > - change "off" to an additional flag, similarly to how it is done > with "max_index_len", and inherit it along with conf->indices. > > > @@ -470,6 +485,22 @@ > > ngx_http_index_t *index; > > ngx_http_script_compile_t sc; > > > > + value = cf->args->elts; > > + > > + if (ngx_strcmp(value[1].data, "off") == 0) { > > + if (cf->args->nelts == 2) { > > + > > + ilcf->off = 1; > > + > > + return NGX_CONF_OK; > > + } else { > > + > > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > > + "\"index off\" must be the only parameter"); > > + return NGX_CONF_ERROR; > > + } > > + } > > + > > Consider the following configuration: > > index index.html; > index off; > > It is expected to be equivalent to "index index.html off;" (if > supported; currently it is), yet these behave very differently > with your code: multiple directives will switch off indexing and > will silently ignore "index.html", yet "index index.html off;" > will try to access the "off" index file. > > Similarly, > > index default.html; > index off index.html; > > is not equivalent to "index default.html off index.html;" and will > be rejected by your code. > > (Also, there are multiple style issues here: wrong indentation, > inconsistent empty lines.) > > > if (ilcf->indices == NULL) { > > ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); > > if (ilcf->indices == NULL) { > > @@ -477,8 +508,6 @@ > > } > > } > > > > - value = cf->args->elts; > > - > > for (i = 1; i < cf->args->nelts; i++) { > > > > if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { > > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.h freenginx-1.30.0_patched/src/http/ngx_http_core_module.h >ngx_http_core_module.h.patch > > --- freenginx-1.30.0/src/http/ngx_http_core_module.h 2026-04-14 05:23:27.000000000 -0300 > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.h 2026-04-28 19:56:25.382446864 -0300 > > @@ -406,6 +406,7 @@ > > ngx_flag_t port_in_redirect; /* port_in_redirect */ > > ngx_flag_t msie_padding; /* msie_padding */ > > ngx_flag_t msie_refresh; /* msie_refresh */ > > + ngx_flag_t log_forbidden; /* log_forbidden */ > > ngx_flag_t log_not_found; /* log_not_found */ > > ngx_flag_t log_subrequest; /* log_subrequest */ > > ngx_flag_t recursive_error_pages; /* recursive_error_pages */ > > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_access_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c >ngx_http_access_module.c.patch > > --- freenginx-1.30.0/src/http/modules/ngx_http_access_module.c 2026-04-14 05:23:27.000000000 -0300 > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c 2026-04-28 19:56:52.838228281 -0300 > > @@ -280,7 +280,7 @@ > > if (deny) { > > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > > - if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { > > + if ((clcf->log_forbidden) && (clcf->satisfy == NGX_HTTP_SATISFY_ALL)) { > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > "access forbidden by rule"); > > } > > Hope this helps. Thank you for your time and help!! As you mentioned, the "log_forbidden" change is completely orthogonal, so let's set it aside for now. While searching for a code pattern in the freenginx codebase to guide my implementation, I accidentally discovered a bug in ngx_http_log_module (ngx_http_log_open_file_cache) when testing the "off" parameter in different positions. For example, the configuration "open_log_file_cache off max=1;" should fail, but it does not. I hope this patch works well! Thank you, again! Fabiano Furtado -------------- next part -------------- A non-text attachment was scrubbed... Name: ngx_http_index_module.c.patch Type: text/x-patch Size: 3735 bytes Desc: not available URL: From mdounin at mdounin.ru Tue May 5 09:16:15 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 5 May 2026 12:16:15 +0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hello! On Sun, May 03, 2026 at 10:22:16PM -0300, Fabiano Furtado wrote: > On Thu, Apr 30, 2026 at 12:34?AM Maxim Dounin wrote: > > > > On Tue, Apr 28, 2026 at 08:32:32PM -0300, Fabiano Furtado wrote: > > > > > On Mon, Apr 27, 2026 at 9:04?PM Maxim Dounin wrote: > > > > > > > > On Mon, Apr 27, 2026 at 09:22:59 AM -0300, Fabiano Furtado wrote: > > > > > > > > > On Sun, Apr 26, 2026 at 7:53?PM Maxim Dounin wrote: > > > > > > ... > > > > > Anyway, I'm not sure if it's possible, but I'd like to try developing > > > > > this patch. Would it be okay if I tried to create this new patch? > > > > > > > > Yep, feel free to. > > > > > > > > Just in case, some hints can be found here: > > > > https://freenginx.org/en/docs/contributing_changes.html > > > > > > This patch introduces the "index off;" directive for freenginx, but I > > > also had to modify other parts of the code to ensure this new > > > directive behaves correctly. > > > > Please make sure to read the above link. In particular, following > > the coding style and using Mercurial is highly recommended, as it > > makes it much easier to review the code. > > > > > When "off" is applied in the config and an HTTP access occurs, a > > > message appears in the error_log: "directory index of ".../" is > > > forbidden". In this context, I believe this message is not > > > particularly relevant for the error_log. So, I added a flag to the > > > core called "log_forbidden", which works similarly to "log_not_found". > > > > I believe this change is completely orthogonal and has nothing to > > do with the "index off;". The message is perfectly identical with > > both "index off;" and the "index .non_such_file;" workaround, so > > "index off;" introduces nothing new here. Also, the message can > > be suppressed with "location ~ /$ { return 403; }" if no indexing > > is indeed desired. > > > > That is, if at all, this should be a separate patch, explaining > > the change and reasons for it. > > > > > As a result, instead of modifying only "ngx_http_index_module.c", I > > > ended up changing four files: > > > * src/http/ngx_http_core_module.c > > > * src/http/ngx_http_core_module.h > > > * src/http/modules/ngx_http_access_module.c > > > * src/http/modules/ngx_http_index_module.c > > > > > > I would appreciate it if the code could be reviewed. Any feedback or > > > suggestions are welcome. > > > > > > Thank you in advance for the opportunity to submit this patch. > > > Fabiano Furtado > > > > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.c freenginx-1.30.0_patched/src/http/ngx_http_core_module.c >ngx_http_core_module.c.patch > > > --- freenginx-1.30.0/src/http/ngx_http_core_module.c 2026-04-14 05:23:27.000000000 -0300 > > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.c 2026-04-28 19:56:18.478687257 -0300 > > > @@ -628,7 +628,14 @@ > > > offsetof(ngx_http_core_loc_conf_t, msie_refresh), > > > NULL }, > > > > > > - { ngx_string("log_not_found"), > > > + { ngx_string("log_forbidden"), > > > + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, > > > + ngx_conf_set_flag_slot, > > > + NGX_HTTP_LOC_CONF_OFFSET, > > > + offsetof(ngx_http_core_loc_conf_t, log_forbidden), > > > + NULL }, > > > + > > > + { ngx_string("log_not_found"), > > > > Note that "log_not_found" in diff suggests there is a white space > > damage. And, indeed, the line lost one of the leading spaces. > > > > Also, in many cases it is a good idea to define new directives > > after the one you want to mimic, and not before it. > > > > > NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, > > > ngx_conf_set_flag_slot, > > > NGX_HTTP_LOC_CONF_OFFSET, > > > @@ -1177,7 +1184,8 @@ > > > ngx_http_core_post_access_phase(ngx_http_request_t *r, > > > ngx_http_phase_handler_t *ph) > > > { > > > - ngx_int_t access_code; > > > + ngx_int_t access_code; > > > + ngx_http_core_loc_conf_t *clcf; > > > > > > ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, > > > "post access phase: %ui", r->phase_handler); > > > @@ -1187,7 +1195,9 @@ > > > if (access_code) { > > > r->access_code = 0; > > > > > > - if (access_code == NGX_HTTP_FORBIDDEN) { > > > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > + > > > + if ((clcf->log_forbidden) && (access_code == NGX_HTTP_FORBIDDEN)) { > > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > > "access forbidden by rule"); > > > } > > > > Requests rejected by the access phase handlers are generally > > expected to be logged. Also, there are various other cases when > > access phase handlers log errors, such as in the > > ngx_http_auth_basic_module on user/password mismatch. > > > > While changing this is possible and I can't say I strongly object > > it, I would like to ensure that this is done in some consistent > > manner, affecting all the standard access modules, and not just > > the errors typically returned by the access module. > > > > Note well that there are various other places where > > NGX_HTTP_FORBIDDEN can be returned, and these log errors, > > including with your patch - autoindex, random index, flv, mp4, and > > static modules all log errors and return NGX_HTTP_FORBIDDEN when > > the file/directory cannot be opened due to OS-level access > > restrictions, and your patch only changes relevant code in the > > index module. It is not immediately clear how these are different > > if the "log_forbidden" directive is expected to behave like > > "log_not_found". And, at the same time, it might be seen as a > > configuration error when a file which cannot be opened by > > [free]nginx is accessible under the document root, so not logging > > the error might be the wrong way to go. > > > > Also, it might be a better idea to follow the log limiting > > approach as used by the limit_req and limit_conn modules, that is, > > introduce some directives similar to limit_req_log_level. > > > > Not well that there is a couple of style issues in the code > > suggested: it should be faster to check "access_code == > > NGX_HTTP_FORBIDDEN" first, similarly to how clcf->log_not_found is > > checked in the static module, and there is no need to place extra > > parentheses where execution order is well defined and immediately > > obvious. > > > > > @@ -1273,9 +1283,10 @@ > > > ngx_http_core_content_phase(ngx_http_request_t *r, > > > ngx_http_phase_handler_t *ph) > > > { > > > - size_t root; > > > - ngx_int_t rc; > > > - ngx_str_t path; > > > + size_t root; > > > + ngx_int_t rc; > > > + ngx_str_t path; > > > + ngx_http_core_loc_conf_t *clcf; > > > > > > if (r->content_handler) { > > > r->write_event_handler = ngx_http_request_empty_handler; > > > @@ -1306,7 +1317,11 @@ > > > > > > if (r->uri.data[r->uri.len - 1] == '/') { > > > > > > - if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) { > > > + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > + > > > + if ((clcf->log_forbidden) > > > + && (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL)) { > > > + > > > > Style issues here: unneeded parentheses, wrong indentation of the > > second line, "{" after multiline conditions should be on its own > > line. > > > > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > > "directory index of \"%s\" is forbidden", path.data); > > > } > > > @@ -3649,6 +3664,7 @@ > > > clcf->port_in_redirect = NGX_CONF_UNSET; > > > clcf->msie_padding = NGX_CONF_UNSET; > > > clcf->msie_refresh = NGX_CONF_UNSET; > > > + clcf->log_forbidden = NGX_CONF_UNSET; > > > clcf->log_not_found = NGX_CONF_UNSET; > > > clcf->log_subrequest = NGX_CONF_UNSET; > > > clcf->recursive_error_pages = NGX_CONF_UNSET; > > > @@ -3923,6 +3939,7 @@ > > > ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1); > > > ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1); > > > ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0); > > > + ngx_conf_merge_value(conf->log_forbidden, prev->log_forbidden, 1); > > > ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1); > > > ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0); > > > ngx_conf_merge_value(conf->recursive_error_pages, > > > > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_index_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c >ngx_http_index_module.c.patch > > > --- freenginx-1.30.0/src/http/modules/ngx_http_index_module.c 2026-04-14 05:23:27.000000000 -0300 > > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_index_module.c 2026-04-28 20:07:00.825547974 -0300 > > > @@ -20,6 +20,7 @@ > > > typedef struct { > > > ngx_array_t *indices; /* array of ngx_http_index_t */ > > > size_t max_index_len; > > > + ngx_flag_t off; > > > } ngx_http_index_loc_conf_t; > > > > > > > > > @@ -109,6 +110,12 @@ > > > ngx_http_index_loc_conf_t *ilcf; > > > ngx_http_script_len_code_pt lcode; > > > > > > + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > > + > > > + if (ilcf->off) { > > > + return NGX_DECLINED; > > > + } > > > + > > > if (r->uri.data[r->uri.len - 1] != '/') { > > > return NGX_DECLINED; > > > } > > > > I believe checking r->uri before and method before even trying to > > access the configuration is optimal from performance point of > > view, and this is what index module does now, and other modules do > > as well (see autoindex or random index for some examples). There > > is no need to change this order. > > > > > @@ -117,7 +124,6 @@ > > > return NGX_DECLINED; > > > } > > > > > > - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > > > > allocated = 0; > > > @@ -369,8 +375,10 @@ > > > u_char *file, ngx_err_t err) > > > { > > > if (err == NGX_EACCES) { > > > - ngx_log_error(NGX_LOG_ERR, r->connection->log, err, > > > - "\"%s\" is forbidden", file); > > > + if (clcf->log_forbidden) { > > > + ngx_log_error(NGX_LOG_ERR, r->connection->log, err, > > > + "\"%s\" is forbidden", file); > > > + } > > > > > > return NGX_HTTP_FORBIDDEN; > > > } > > > > Note that this change should be in a separate patch, and see above > > for more comments about this. > > > > > @@ -396,6 +404,7 @@ > > > > > > conf->indices = NULL; > > > conf->max_index_len = 0; > > > + conf->off = NGX_CONF_UNSET; > > > > > > return conf; > > > } > > > @@ -409,6 +418,12 @@ > > > > > > ngx_http_index_t *index; > > > > > > + ngx_conf_merge_value(conf->off, prev->off, 0); > > > + > > > + if (conf->off) { > > > + return NGX_CONF_OK; > > > + } > > > + > > > if (conf->indices == NULL) { > > > conf->indices = prev->indices; > > > conf->max_index_len = prev->max_index_len; > > > > Consider the following configuration: > > > > server { > > index off; > > > > location /public/ { > > index index.html; > > } > > } > > > > With the merge logic you've implemented this will result in > > "conf->off" being set in "location /public/", leading to no index > > files being used there, which is obviously not the requested > > behaviour. > > > > There are multiple possible solutions here: > > > > - write a custom merge logic, checking both conf->off and > > conf->indices and inheriting conf->off and conf->indices only if > > neither of the two was set at the current level, > > > > - change the code to instead rely on conf->indices special values > > (for example, conf->indices == NGX_CONF_UNSET_PTR for not set, > > and NULL for off; or indices->nelts == 0 for off), > > > > - change "off" to an additional flag, similarly to how it is done > > with "max_index_len", and inherit it along with conf->indices. > > > > > @@ -470,6 +485,22 @@ > > > ngx_http_index_t *index; > > > ngx_http_script_compile_t sc; > > > > > > + value = cf->args->elts; > > > + > > > + if (ngx_strcmp(value[1].data, "off") == 0) { > > > + if (cf->args->nelts == 2) { > > > + > > > + ilcf->off = 1; > > > + > > > + return NGX_CONF_OK; > > > + } else { > > > + > > > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > > > + "\"index off\" must be the only parameter"); > > > + return NGX_CONF_ERROR; > > > + } > > > + } > > > + > > > > Consider the following configuration: > > > > index index.html; > > index off; > > > > It is expected to be equivalent to "index index.html off;" (if > > supported; currently it is), yet these behave very differently > > with your code: multiple directives will switch off indexing and > > will silently ignore "index.html", yet "index index.html off;" > > will try to access the "off" index file. > > > > Similarly, > > > > index default.html; > > index off index.html; > > > > is not equivalent to "index default.html off index.html;" and will > > be rejected by your code. > > > > (Also, there are multiple style issues here: wrong indentation, > > inconsistent empty lines.) > > > > > if (ilcf->indices == NULL) { > > > ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); > > > if (ilcf->indices == NULL) { > > > @@ -477,8 +508,6 @@ > > > } > > > } > > > > > > - value = cf->args->elts; > > > - > > > for (i = 1; i < cf->args->nelts; i++) { > > > > > > if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { > > > > > # diff -Nau freenginx-1.30.0/src/http/ngx_http_core_module.h freenginx-1.30.0_patched/src/http/ngx_http_core_module.h >ngx_http_core_module.h.patch > > > --- freenginx-1.30.0/src/http/ngx_http_core_module.h 2026-04-14 05:23:27.000000000 -0300 > > > +++ freenginx-1.30.0_patched/src/http/ngx_http_core_module.h 2026-04-28 19:56:25.382446864 -0300 > > > @@ -406,6 +406,7 @@ > > > ngx_flag_t port_in_redirect; /* port_in_redirect */ > > > ngx_flag_t msie_padding; /* msie_padding */ > > > ngx_flag_t msie_refresh; /* msie_refresh */ > > > + ngx_flag_t log_forbidden; /* log_forbidden */ > > > ngx_flag_t log_not_found; /* log_not_found */ > > > ngx_flag_t log_subrequest; /* log_subrequest */ > > > ngx_flag_t recursive_error_pages; /* recursive_error_pages */ > > > > > # diff -Nau freenginx-1.30.0/src/http/modules/ngx_http_access_module.c freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c >ngx_http_access_module.c.patch > > > --- freenginx-1.30.0/src/http/modules/ngx_http_access_module.c 2026-04-14 05:23:27.000000000 -0300 > > > +++ freenginx-1.30.0_patched/src/http/modules/ngx_http_access_module.c 2026-04-28 19:56:52.838228281 -0300 > > > @@ -280,7 +280,7 @@ > > > if (deny) { > > > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > > > > - if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) { > > > + if ((clcf->log_forbidden) && (clcf->satisfy == NGX_HTTP_SATISFY_ALL)) { > > > ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, > > > "access forbidden by rule"); > > > } > > > > Hope this helps. > > Thank you for your time and help!! > > As you mentioned, the "log_forbidden" change is completely orthogonal, > so let's set it aside for now. Good. > While searching for a code pattern in the freenginx codebase to guide > my implementation, I accidentally discovered a bug in > ngx_http_log_module (ngx_http_log_open_file_cache) when testing the > "off" parameter in different positions. For example, the configuration > "open_log_file_cache off max=1;" should fail, but it does not. Most of the named argument directive parsers are somewhat sloppy - in particular, they fail to detect duplicate arguments (such as in "open_file_cache max=1 max=2;") and often fail to detect conflicting arguments, as in your example. > I hope this patch works well! Thank you, again! > Fabiano Furtado > diff -r f347a195b373 src/http/modules/ngx_http_index_module.c > --- a/src/http/modules/ngx_http_index_module.c Thu Apr 30 07:25:52 2026 +0300 > +++ b/src/http/modules/ngx_http_index_module.c Sun May 03 22:15:23 2026 -0300 > @@ -20,6 +20,7 @@ > typedef struct { > ngx_array_t *indices; /* array of ngx_http_index_t */ > size_t max_index_len; > + ngx_flag_t off; The "ngx_flag_t" type is used for configuration directives with the ngx_conf_set_flag_slot() handler. There is no need to use this type for fields which aren't with ngx_conf_set_flag_slot(). In this particular case, just one bit should be enough. But since there are no other bit fields in the structure, there is no need to declare it as a bit field, as there will be no memory benefits, and might be performance degradation. Existing approach for such cases is to use the ngx_uint_t type and provide corresponding bit field type in a comment, e.g.: ngx_uint_t off; /* unsigned:1 */ You can see an example in the access log module you've mentioned. > } ngx_http_index_loc_conf_t; > > > @@ -109,6 +110,12 @@ > ngx_http_index_loc_conf_t *ilcf; > ngx_http_script_len_code_pt lcode; > > + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > + > + if (ilcf->off) { > + return NGX_DECLINED; > + } > + > if (r->uri.data[r->uri.len - 1] != '/') { > return NGX_DECLINED; > } You've probably missed the comment about this code in the previous review: > > I believe checking r->uri before and method before even trying to > > access the configuration is optimal from performance point of > > view, and this is what index module does now, and other modules do > > as well (see autoindex or random index for some examples). There > > is no need to change this order. > @@ -117,7 +124,6 @@ > return NGX_DECLINED; > } > > - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > allocated = 0; > @@ -396,6 +402,7 @@ > > conf->indices = NULL; > conf->max_index_len = 0; > + conf->off = 0; > > return conf; > } > @@ -409,31 +416,34 @@ > > ngx_http_index_t *index; > > - if (conf->indices == NULL) { > - conf->indices = prev->indices; > - conf->max_index_len = prev->max_index_len; > + if (conf->indices || conf->off) { > + return NGX_CONF_OK; > + } > + > + conf->indices = prev->indices; > + conf->max_index_len = prev->max_index_len; > + conf->off = prev->off; > + > + if (conf->indices || conf->off) { > + return NGX_CONF_OK; > } In general, an early return from a merge function is better avoided: it makes extending the function non-trivial, so introducing additional configuration directives will be complicated. > > + conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); > if (conf->indices == NULL) { > - conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); > - if (conf->indices == NULL) { > - return NGX_CONF_ERROR; > - } > + return NGX_CONF_ERROR; > + } > > - index = ngx_array_push(conf->indices); > - if (index == NULL) { > - return NGX_CONF_ERROR; > - } > + index = ngx_array_push(conf->indices); > + if (index == NULL) { > + return NGX_CONF_ERROR; > + } > > - index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX); > - index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX; > - index->lengths = NULL; > - index->values = NULL; > + index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX); > + index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX; > + index->lengths = NULL; > + index->values = NULL; > > - conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); > - > - return NGX_CONF_OK; > - } > + conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > return NGX_CONF_OK; > } > @@ -470,6 +480,28 @@ > ngx_http_index_t *index; > ngx_http_script_compile_t sc; > > + value = cf->args->elts; > + > + if (ngx_strcmp(value[1].data, "off") == 0) { > + if (cf->args->nelts == 2) { > + ilcf->off = 1; > + return NGX_CONF_OK; > + } > + > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > + "invalid parameter \"%V\"", &value[2]); > + return NGX_CONF_ERROR; > + } > + > + for (i = 2; i < cf->args->nelts; i++) { > + > + if (ngx_strcmp(value[i].data, "off") == 0) { > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > + "parameter \"off\" must be declared first"); > + return NGX_CONF_ERROR; > + } > + } I believe there is no need for an additional loop here, we already have one (and it already does a couple of similar checks). > + > if (ilcf->indices == NULL) { > ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); > if (ilcf->indices == NULL) { > @@ -477,8 +509,6 @@ > } > } > > - value = cf->args->elts; > - > for (i = 1; i < cf->args->nelts; i++) { > > if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { Overall, looking through a couple of variants I tend to think that using licf->indices == NULL as a special value would be optimal here. Suggested patch below. # HG changeset patch # User Maxim Dounin # Date 1777971559 -10800 # Tue May 05 11:59:19 2026 +0300 # Node ID e8bd179e5e7be4e6422918dbc6cace143de7b8ce # Parent f347a195b373c1d118fd8a929886692f052e8d4b Index: added "index off;". This might be useful to save a syscall if index files should not be used, for example, when autoindex is expected to be used instead. Prodded by Fabiano Furtado. 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 @@ -120,6 +120,10 @@ ngx_http_index_handler(ngx_http_request_ ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (ilcf->indices == NULL) { + return NGX_DECLINED; + } + allocated = 0; root = 0; dir_tested = 0; @@ -394,7 +398,7 @@ ngx_http_index_create_loc_conf(ngx_conf_ return NULL; } - conf->indices = NULL; + conf->indices = NGX_CONF_UNSET_PTR; conf->max_index_len = 0; return conf; @@ -409,12 +413,12 @@ ngx_http_index_merge_loc_conf(ngx_conf_t ngx_http_index_t *index; - if (conf->indices == NULL) { + if (conf->indices == NGX_CONF_UNSET_PTR) { conf->indices = prev->indices; conf->max_index_len = prev->max_index_len; } - if (conf->indices == NULL) { + if (conf->indices == NGX_CONF_UNSET_PTR) { conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); if (conf->indices == NULL) { return NGX_CONF_ERROR; @@ -470,15 +474,30 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_http_index_t *index; ngx_http_script_compile_t sc; + value = cf->args->elts; + + if (cf->args->nelts == 2 + && ngx_strcmp(value[1].data, "off") == 0) + { + if (ilcf->indices != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + ilcf->indices = NULL; + return NGX_CONF_OK; + } + if (ilcf->indices == NULL) { + return "is duplicate"; + } + + if (ilcf->indices == NGX_CONF_UNSET_PTR) { ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); if (ilcf->indices == NULL) { return NGX_CONF_ERROR; } } - value = cf->args->elts; - for (i = 1; i < cf->args->nelts; i++) { if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { @@ -494,6 +513,13 @@ ngx_http_index_set_index(ngx_conf_t *cf, return NGX_CONF_ERROR; } + if (ngx_strcmp(value[i].data, "off") == 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "index \"%V\" in \"index\" directive is invalid", + &value[i]); + return NGX_CONF_ERROR; + } + index = ngx_array_push(ilcf->indices); if (index == NULL) { return NGX_CONF_ERROR; # HG changeset patch # User Maxim Dounin # Date 1777971696 -10800 # Tue May 05 12:01:36 2026 +0300 # Node ID 6feef2c09b82aede49cb96ae8ddcf1dce37c5c67 # Parent e8bd179e5e7be4e6422918dbc6cace143de7b8ce Index: fixed error message about empty index. 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 @@ -509,7 +509,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, if (value[i].len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "index \"%V\" in \"index\" directive is invalid", - &value[1]); + &value[i]); return NGX_CONF_ERROR; } -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue May 5 10:27:35 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 5 May 2026 13:27:35 +0300 Subject: freenginx-1.31.0 changes draft Message-ID: Hello! Changes with freenginx 1.31.0 05 May 2026 *) Change: now the "try_files" directive can be used with arguments starting with the request URI inside a location given by a regular expression, along with the "alias" directive. *) Bugfix: the "try_files" directive might work incorrectly when used with the "alias" directive. *) Bugfix: a segmentation fault might occur in a worker process if the "try_files" and "proxy_pass" directives were used together. *) Bugfix: the "proxy_pass" directive without a URI part might use the original request URI after the URI was changed by the "try_files" directive. *) Change: the logging level of the "invalid ccs message", "not on record boundary", "required compression algorithm missing", and some "record layer failure" SSL errors has been lowered from "crit" to "info". ????????? ? freenginx 1.31.0 05.05.2026 *) ?????????: ?????? ????????? try_files ????? ???????????? ? ???????????, ????????????? ?? URI ???????, ?????? location, ????????? ?????????? ??????????, ?????? ? ?????????? alias. *) ???????????: ????????? try_files ????? ???????? ??????????? ??? ?????????? ????????????? ? ?????????? alias. *) ???????????: ??? ?????????? ????????????? ???????? try_files ? proxy_pass ? ??????? ???????? ??? ????????? segmentation fault. *) ???????????: ????? ????????? URI ??????? ? ??????? ????????? try_files ????????? proxy_pass ??? URI ????? ???????????? URI ????????? ???????. *) ?????????: ??????? ???????????? ?????? SSL "invalid ccs message", "not on record boundary", "required compression algorithm missing" ? ????????? ?????? "record layer failure" ??????? ? ?????? crit ?? info. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue May 5 13:04:04 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 05 May 2026 16:04:04 +0300 Subject: [nginx] Updated OpenSSL used for win32 builds. Message-ID: details: http://freenginx.org/hg/nginx/rev/306497e897a5 branches: changeset: 9509:306497e897a5 user: Maxim Dounin date: Tue May 05 14:05:36 2026 +0300 description: Updated OpenSSL used for win32 builds. diffstat: misc/GNUmakefile | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/misc/GNUmakefile b/misc/GNUmakefile --- a/misc/GNUmakefile +++ b/misc/GNUmakefile @@ -6,7 +6,7 @@ TEMP = tmp CC = cl OBJS = objs.msvc8 -OPENSSL = openssl-3.0.19 +OPENSSL = openssl-3.5.6 ZLIB = zlib-1.3.2 PCRE = pcre2-10.47 From mdounin at mdounin.ru Tue May 5 13:04:04 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 05 May 2026 16:04:04 +0300 Subject: [nginx] freenginx-1.31.0-RELEASE Message-ID: details: http://freenginx.org/hg/nginx/rev/b1585cfeee57 branches: changeset: 9510:b1585cfeee57 user: Maxim Dounin date: Tue May 05 15:59:22 2026 +0300 description: freenginx-1.31.0-RELEASE diffstat: docs/xml/nginx/changes.xml | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 72 insertions(+), 0 deletions(-) diffs (82 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,78 @@
+ + + + +?????? ????????? try_files ????? ???????????? +? ???????????, ????????????? ?? URI ???????, +?????? location, ????????? ?????????? ??????????, +?????? ? ?????????? alias. + + +now the "try_files" directive can be used +with arguments starting with the request URI +inside a location given by a regular expression, +along with the "alias" directive. + + + + + +????????? try_files ????? ???????? ??????????? +??? ?????????? ????????????? ? ?????????? alias. + + +the "try_files" directive might work incorrectly +when used with the "alias" directive. + + + + + +??? ?????????? ????????????? ???????? try_files ? proxy_pass +? ??????? ???????? ??? ????????? segmentation fault. + + +a segmentation fault might occur in a worker process +if the "try_files" and "proxy_pass" directives were used together. + + + + + +????? ????????? URI ??????? ? ??????? ????????? try_files +????????? proxy_pass ??? URI +????? ???????????? URI ????????? ???????. + + +the "proxy_pass" directive without a URI part +might use the original request URI +after the URI was changed by the "try_files" directive. + + + + + +??????? ???????????? ?????? SSL +"invalid ccs message", "not on record boundary", +"required compression algorithm missing" +? ????????? ?????? "record layer failure" +??????? ? ?????? crit ?? info. + + +the logging level of the +"invalid ccs message", "not on record boundary", +"required compression algorithm missing", +and some "record layer failure" SSL errors +has been lowered from "crit" to "info". + + + + + + From mdounin at mdounin.ru Tue May 5 13:04:04 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 05 May 2026 16:04:04 +0300 Subject: [nginx] release-1.31.0 tag Message-ID: details: http://freenginx.org/hg/nginx/rev/59e7fc2193b4 branches: changeset: 9511:59e7fc2193b4 user: Maxim Dounin date: Tue May 05 15:59:23 2026 +0300 description: release-1.31.0 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -493,3 +493,4 @@ 567870bfeb23f2e9c91e2a110d6d332c27c1ceb1 4f4280557d20bc46ebbdc240ffd365f5ca6ce939 release-1.29.5 e4207f631186855d37ac286799c8cd4c9477d166 release-1.29.6 cac0fa5721386abbec57dcc2bb317f2531456e19 release-1.29.7 +b1585cfeee5759a1ae3794b5c9c4c541c3c73a35 release-1.31.0 From mdounin at mdounin.ru Tue May 5 13:04:33 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 05 May 2026 16:04:33 +0300 Subject: [nginx-site] freenginx-1.31.0 Message-ID: details: http://freenginx.org/hg/nginx-site/rev/ba01a18ef6e8 branches: changeset: 3132:ba01a18ef6e8 user: Maxim Dounin date: Tue May 05 16:03:02 2026 +0300 description: freenginx-1.31.0 diffstat: text/en/CHANGES | 22 ++++++++++++++++++++++ text/ru/CHANGES.ru | 22 ++++++++++++++++++++++ xml/index.xml | 10 ++++++++++ xml/versions.xml | 2 +- 4 files changed, 55 insertions(+), 1 deletions(-) diffs (92 lines): diff --git a/text/en/CHANGES b/text/en/CHANGES --- a/text/en/CHANGES +++ b/text/en/CHANGES @@ -1,4 +1,26 @@ +Changes with freenginx 1.31.0 05 May 2026 + + *) Change: now the "try_files" directive can be used with arguments + starting with the request URI inside a location given by a regular + expression, along with the "alias" directive. + + *) Bugfix: the "try_files" directive might work incorrectly when used + with the "alias" directive. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "try_files" and "proxy_pass" directives were used together. + + *) Bugfix: the "proxy_pass" directive without a URI part might use the + original request URI after the URI was changed by the "try_files" + directive. + + *) Change: the logging level of the "invalid ccs message", "not on + record boundary", "required compression algorithm missing", and some + "record layer failure" SSL errors has been lowered from "crit" to + "info". + + Changes with freenginx 1.29.7 31 Mar 2026 *) Feature: OpenSSL 4.0 compatibility. diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru @@ -1,4 +1,26 @@ +????????? ? freenginx 1.31.0 05.05.2026 + + *) ?????????: ?????? ????????? try_files ????? ???????????? ? + ???????????, ????????????? ?? URI ???????, ?????? location, ????????? + ?????????? ??????????, ?????? ? ?????????? alias. + + *) ???????????: ????????? try_files ????? ???????? ??????????? ??? + ?????????? ????????????? ? ?????????? alias. + + *) ???????????: ??? ?????????? ????????????? ???????? try_files ? + proxy_pass ? ??????? ???????? ??? ????????? segmentation fault. + + *) ???????????: ????? ????????? URI ??????? ? ??????? ????????? + try_files ????????? proxy_pass ??? URI ????? ???????????? URI + ????????? ???????. + + *) ?????????: ??????? ???????????? ?????? SSL "invalid ccs message", + "not on record boundary", "required compression algorithm missing" ? + ????????? ?????? "record layer failure" ??????? ? ?????? crit ?? + info. + + ????????? ? freenginx 1.29.7 31.03.2026 *) ??????????: ????????????? ? OpenSSL 4.0. diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,16 @@ + + +freenginx-1.31.0 +mainline version has been released, +with + +fixes and improvements. + + + freenginx-1.30.0 diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -9,7 +9,7 @@ - + From fabianofurtado at gmail.com Wed May 6 01:05:36 2026 From: fabianofurtado at gmail.com (Fabiano Furtado) Date: Tue, 5 May 2026 22:05:36 -0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: HI! On Tue, May 5, 2026 at 6:16?AM Maxim Dounin wrote: > > Hello! > ... > Good. > > > While searching for a code pattern in the freenginx codebase to guide > > my implementation, I accidentally discovered a bug in > > ngx_http_log_module (ngx_http_log_open_file_cache) when testing the > > "off" parameter in different positions. For example, the configuration > > "open_log_file_cache off max=1;" should fail, but it does not. > > Most of the named argument directive parsers are somewhat sloppy - > in particular, they fail to detect duplicate arguments (such as in > "open_file_cache max=1 max=2;") and often fail to detect > conflicting arguments, as in your example. OK. > > I hope this patch works well! Thank you, again! > > Fabiano Furtado > > > diff -r f347a195b373 src/http/modules/ngx_http_index_module.c > > --- a/src/http/modules/ngx_http_index_module.c Thu Apr 30 07:25:52 2026 +0300 > > +++ b/src/http/modules/ngx_http_index_module.c Sun May 03 22:15:23 2026 -0300 > > @@ -20,6 +20,7 @@ > > typedef struct { > > ngx_array_t *indices; /* array of ngx_http_index_t */ > > size_t max_index_len; > > + ngx_flag_t off; > > The "ngx_flag_t" type is used for configuration directives with > the ngx_conf_set_flag_slot() handler. There is no need to use > this type for fields which aren't with ngx_conf_set_flag_slot(). > > In this particular case, just one bit should be enough. But since > there are no other bit fields in the structure, there is no need > to declare it as a bit field, as there will be no memory benefits, > and might be performance degradation. Existing approach for such > cases is to use the ngx_uint_t type and provide corresponding bit > field type in a comment, e.g.: > > ngx_uint_t off; /* unsigned:1 */ > > You can see an example in the access log module you've mentioned. > > > } ngx_http_index_loc_conf_t; > > > > > > @@ -109,6 +110,12 @@ > > ngx_http_index_loc_conf_t *ilcf; > > ngx_http_script_len_code_pt lcode; > > > > + ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > + > > + if (ilcf->off) { > > + return NGX_DECLINED; > > + } > > + > > if (r->uri.data[r->uri.len - 1] != '/') { > > return NGX_DECLINED; > > } > > You've probably missed the comment about this code in the previous > review: > > > > I believe checking r->uri before and method before even trying to > > > access the configuration is optimal from performance point of > > > view, and this is what index module does now, and other modules do > > > as well (see autoindex or random index for some examples). There > > > is no need to change this order. Sorry! :( > > @@ -117,7 +124,6 @@ > > return NGX_DECLINED; > > } > > > > - ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); > > clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); > > > > allocated = 0; > > @@ -396,6 +402,7 @@ > > > > conf->indices = NULL; > > conf->max_index_len = 0; > > + conf->off = 0; > > > > return conf; > > } > > @@ -409,31 +416,34 @@ > > > > ngx_http_index_t *index; > > > > - if (conf->indices == NULL) { > > - conf->indices = prev->indices; > > - conf->max_index_len = prev->max_index_len; > > + if (conf->indices || conf->off) { > > + return NGX_CONF_OK; > > + } > > + > > + conf->indices = prev->indices; > > + conf->max_index_len = prev->max_index_len; > > + conf->off = prev->off; > > + > > + if (conf->indices || conf->off) { > > + return NGX_CONF_OK; > > } > > In general, an early return from a merge function is better > avoided: it makes extending the function non-trivial, so > introducing additional configuration directives will be > complicated. I based my approach on the merge function in the log module, but I understand. > > + conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); > > if (conf->indices == NULL) { > > - conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); > > - if (conf->indices == NULL) { > > - return NGX_CONF_ERROR; > > - } > > + return NGX_CONF_ERROR; > > + } > > > > - index = ngx_array_push(conf->indices); > > - if (index == NULL) { > > - return NGX_CONF_ERROR; > > - } > > + index = ngx_array_push(conf->indices); > > + if (index == NULL) { > > + return NGX_CONF_ERROR; > > + } > > > > - index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > - index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX; > > - index->lengths = NULL; > > - index->values = NULL; > > + index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > + index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX; > > + index->lengths = NULL; > > + index->values = NULL; > > > > - conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > - > > - return NGX_CONF_OK; > > - } > > + conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > > > return NGX_CONF_OK; > > } > > @@ -470,6 +480,28 @@ > > ngx_http_index_t *index; > > ngx_http_script_compile_t sc; > > > > + value = cf->args->elts; > > + > > + if (ngx_strcmp(value[1].data, "off") == 0) { > > + if (cf->args->nelts == 2) { > > + ilcf->off = 1; > > + return NGX_CONF_OK; > > + } > > + > > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > > + "invalid parameter \"%V\"", &value[2]); > > + return NGX_CONF_ERROR; > > + } > > + > > + for (i = 2; i < cf->args->nelts; i++) { > > + > > + if (ngx_strcmp(value[i].data, "off") == 0) { > > + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, > > + "parameter \"off\" must be declared first"); > > + return NGX_CONF_ERROR; > > + } > > + } > > I believe there is no need for an additional loop here, we already > have one (and it already does a couple of similar checks). OK. > > + > > if (ilcf->indices == NULL) { > > ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); > > if (ilcf->indices == NULL) { > > @@ -477,8 +509,6 @@ > > } > > } > > > > - value = cf->args->elts; > > - > > for (i = 1; i < cf->args->nelts; i++) { > > > > if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { > > Overall, looking through a couple of variants I tend to think that > using licf->indices == NULL as a special value would be optimal > here. Suggested patch below. Yeah! Using indices, you optimize memory and have one less variable to manage in the module. KISS principle! ;) > # HG changeset patch > # User Maxim Dounin > # Date 1777971559 -10800 > # Tue May 05 11:59:19 2026 +0300 > # Node ID e8bd179e5e7be4e6422918dbc6cace143de7b8ce > # Parent f347a195b373c1d118fd8a929886692f052e8d4b > Index: added "index off;". > > This might be useful to save a syscall if index files should not be used, > for example, when autoindex is expected to be used instead. > > Prodded by Fabiano Furtado. Thank you very much for the mention! > 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 > ... > return NGX_CONF_ERROR; > } I'll test your patch, though I'm confident it's already working perfectly. Thanks for your resilience, patience, time and help. I still couldn't produce a working patch, but I've learned a lot, and I'm grateful for that. Fabiano Furtado From mdounin at mdounin.ru Sat May 9 21:01:13 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 10 May 2026 00:01:13 +0300 Subject: [PATCH] SCGI: fixed handling of unbuffered request body Message-ID: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778251703 -10800 # Fri May 08 17:48:23 2026 +0300 # Node ID 8cee4d35c15b7c6d3b788b1608cd13bc2ad52cdb # Parent 874f5f884e0a09f6433f0385ba195a71e94cb5d2 SCGI: fixed handling of unbuffered request body. Previously, size of the request body as sent in the CONTENT_LENGTH param was calculated based on the size of the request buffers available. This is, however, wrong for unbuffered request body, since the request body might not be fully available yet. The fix is to use r->headers_in.content_length_n (as long as it is available and there is a body), much like other modules do. See also: https://github.com/nginx/nginx/commit/ec714d52bd4914d52a113234c16e1855d9ac7dcf 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 @@ -649,11 +649,9 @@ ngx_http_scgi_create_request(ngx_http_re u_char buffer[NGX_OFF_T_LEN]; content_length_n = 0; - body = r->upstream->request_bufs; - - while (body) { - content_length_n += ngx_buf_size(body->buf); - body = body->next; + + if (r->headers_in.content_length_n > 0 && !r->discard_body) { + content_length_n = r->headers_in.content_length_n; } content_length.data = buffer; From mdounin at mdounin.ru Sat May 9 21:02:36 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 10 May 2026 00:02:36 +0300 Subject: [PATCH 1 of 3] Tests: improved SCGI request body tests In-Reply-To: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> References: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1778250844 -10800 # Fri May 08 17:34:04 2026 +0300 # Node ID e5409c48dce25d4d5fe234de8a5f4f4ff9efcee7 # Parent ff476932c9806e6b8b6d9d934186fb9e43aa7aee Tests: improved SCGI request body tests. diff --git a/scgi_body.t b/scgi_body.t --- a/scgi_body.t +++ b/scgi_body.t @@ -2,7 +2,7 @@ # (C) Maxim Dounin -# Test for scgi backend with chunked request body. +# Test for scgi backend with request body. ############################################################################### @@ -57,32 +57,13 @@ EOF ############################################################################### -like(http_get('/'), qr/X-Body: /, 'scgi no body'); - -like(http_get_length('/', ''), qr/X-Body: /, 'scgi empty body'); -like(http_get_length('/', 'foobar'), qr/X-Body: foobar/, 'scgi body'); - -like(http(<connection()->print(< References: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> Message-ID: <20878a3be12b6ff597e9.1778360557@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778251045 -10800 # Fri May 08 17:37:25 2026 +0300 # Node ID 20878a3be12b6ff597e952432f6e8f18fbec549a # Parent e5409c48dce25d4d5fe234de8a5f4f4ff9efcee7 Tests: SCGI tests for non-buffered and discarded request body. diff --git a/scgi_body.t b/scgi_body.t --- a/scgi_body.t +++ b/scgi_body.t @@ -24,7 +24,7 @@ select STDOUT; $| = 1; eval { require SCGI; }; plan(skip_all => 'SCGI not installed') if $@; -my $t = Test::Nginx->new()->has(qw/http scgi/)->plan(5) +my $t = Test::Nginx->new()->has(qw/http scgi/)->plan(7) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -46,6 +46,18 @@ http { scgi_param SCGI 1; scgi_param REQUEST_URI $request_uri; } + + location /nb/ { + scgi_pass 127.0.0.1:8081; + scgi_param SCGI 1; + scgi_param REQUEST_URI $request_uri; + scgi_request_buffering off; + } + + location /discard/ { + client_max_body_size 1; + error_page 413 /error413; + } } } @@ -65,6 +77,17 @@ like(http_get_length('/', ''), qr/X-Body like(http_get_chunked('/', 'foobar'), qr/X-Body: 'foobar'/, 'scgi chunked'); like(http_get_chunked('/', ''), qr/X-Body: ''/, 'scgi empty chunked'); +TODO: { +local $TODO = 'not yet' unless $t->has_version('1.31.1'); + +like(http_get_nobuf('/nb/', 'foo', 'bar'), qr/X-Body: 'foobar'/, + 'scgi no request buffering'); + +} + +like(http_get_nobuf('/discard/', 'foo', 'bar'), qr/X-Body: ''/, + 'scgi discard body'); + ############################################################################### sub http_get_length { @@ -96,6 +119,18 @@ 0 EOF } +sub http_get_nobuf { + my ($url, $body1, $body2) = @_; + my $length = length($body1) + length($body2); + return http(< 0.1, body => $body2); +GET $url HTTP/1.1 +Host: localhost +Connection: close +Content-Length: $length + +EOF +} + ############################################################################### sub scgi_daemon { From mdounin at mdounin.ru Sat May 9 21:02:38 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 10 May 2026 00:02:38 +0300 Subject: [PATCH 3 of 3] Tests: uwsgi tests for non-buffered and discarded request body In-Reply-To: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> References: <8cee4d35c15b7c6d3b78.1778360473@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1778251716 -10800 # Fri May 08 17:48:36 2026 +0300 # Node ID b4abfc864a24f2d06bcab10a89655a47b19562e3 # Parent 20878a3be12b6ff597e952432f6e8f18fbec549a Tests: uwsgi tests for non-buffered and discarded request body. diff --git a/uwsgi_body.t b/uwsgi_body.t --- a/uwsgi_body.t +++ b/uwsgi_body.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -23,7 +24,7 @@ select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite uwsgi/) - ->has_daemon('uwsgi')->plan(5) + ->has_daemon('uwsgi')->plan(7) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -46,6 +47,17 @@ http { uwsgi_pass 127.0.0.1:8081; uwsgi_param CONTENT_LENGTH $content_length if_not_empty; } + + location /nb/ { + uwsgi_pass 127.0.0.1:8081; + uwsgi_param CONTENT_LENGTH $content_length if_not_empty; + uwsgi_request_buffering off; + } + + location /discard/ { + client_max_body_size 1; + error_page 413 /error413; + } } } @@ -95,6 +107,12 @@ like(http_get_length('/', ''), qr/cl=0 ' like(http_get_chunked('/', 'foobar'), qr/cl=6 'foobar'/, 'uwsgi chunked'); like(http_get_chunked('/', ''), qr/cl=0 ''/, 'uwsgi empty chunked'); +like(http_get_nobuf('/', 'foo', 'bar'), qr/cl=6 'foobar'/, + 'uwsgi no request buffering'); + +like(http_get_nobuf('/discard/', 'foo', 'bar'), qr/SEE-THIS/, + 'uwsgi discard body'); + ############################################################################### sub http_get_length { @@ -126,4 +144,16 @@ 0 EOF } +sub http_get_nobuf { + my ($url, $body1, $body2) = @_; + my $length = length($body1) + length($body2); + return http(< 0.1, body => $body2); +GET $url HTTP/1.1 +Host: localhost +Connection: close +Content-Length: $length + +EOF +} + ############################################################################### From mdounin at mdounin.ru Sat May 9 23:15:34 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 10 May 2026 02:15:34 +0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hello! On Tue, May 05, 2026 at 10:05:36PM -0300, Fabiano Furtado wrote: [...] > > # HG changeset patch > > # User Maxim Dounin > > # Date 1777971559 -10800 > > # Tue May 05 11:59:19 2026 +0300 > > # Node ID e8bd179e5e7be4e6422918dbc6cace143de7b8ce > > # Parent f347a195b373c1d118fd8a929886692f052e8d4b > > Index: added "index off;". > > > > This might be useful to save a syscall if index files should not be used, > > for example, when autoindex is expected to be used instead. > > > > Prodded by Fabiano Furtado. > > Thank you very much for the mention! > > > 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 > > ... > > return NGX_CONF_ERROR; > > } > > I'll test your patch, though I'm confident it's already working perfectly. Looking more into this, I think about only checking "off" if there is only one argument. Resulting syntax would be: index off | file ...; With this approach, index off; will switch off index usage, and will reject any attempts to define additional indexes, so index off; index index.html; and index index.html; index off; will be rejected. At the same time, when "index off;" is clearly not the intention due to multiple arguments of the directive, such as in index index.html off on; or index off index.html; all arguments would be considered index file names, exactly as it works now. I tend to think this would be clearer from the syntax point of view. (The patch is essentially the same, but without checking for "off" in the loop.) What do you think? -- Maxim Dounin http://mdounin.ru/ From fabianofurtado at gmail.com Mon May 11 16:54:16 2026 From: fabianofurtado at gmail.com (Fabiano Furtado) Date: Mon, 11 May 2026 13:54:16 -0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hi! On Sat, May 9, 2026 at 8:15?PM Maxim Dounin wrote: > > Hello! > [...] > Looking more into this, I think about only checking "off" if there > is only one argument. Resulting syntax would be: > > index off | file ...; > > With this approach, > > index off; > > will switch off index usage, and will reject any attempts to define > additional indexes, so > > index off; > index index.html; > > and > > index index.html; > index off; > > will be rejected. At the same time, when "index off;" is clearly > not the intention due to multiple arguments of the directive, such > as in > > index index.html off on; > > or > > index off index.html; > > all arguments would be considered index file names, exactly as it > works now. > > I tend to think this would be clearer from the syntax point of > view. > > (The patch is essentially the same, but without checking for "off" > in the loop.) > > What do you think? Removing the "off" check from the loop makes the index configuration more flexible because it allows using "off" as an index file, although this is an extremely rare use case, which I think is OK. ;) Fabiano Furtado From mdounin at mdounin.ru Tue May 12 22:16:26 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 13 May 2026 01:16:26 +0300 Subject: [nginx] Version bump. Message-ID: details: http://freenginx.org/hg/nginx/rev/07830f618125 branches: changeset: 9512:07830f618125 user: Maxim Dounin date: Wed May 13 01:15:22 2026 +0300 description: Version bump. diffstat: src/core/nginx.h | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diffs (14 lines): diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1031000 -#define NGINX_VERSION "1.31.0" +#define nginx_version 1031001 +#define NGINX_VERSION "1.31.1" #define freenginx 1 From mdounin at mdounin.ru Tue May 12 22:16:27 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 13 May 2026 01:16:27 +0300 Subject: [nginx] Index: added "index off;". Message-ID: details: http://freenginx.org/hg/nginx/rev/956661df1555 branches: changeset: 9513:956661df1555 user: Maxim Dounin date: Wed May 13 01:15:27 2026 +0300 description: Index: added "index off;". This might be useful to save a syscall if index files should not be used, for example, when autoindex is expected to be used instead. Prodded by Fabiano Furtado. diffstat: src/http/modules/ngx_http_index_module.c | 29 ++++++++++++++++++++++++----- 1 files changed, 24 insertions(+), 5 deletions(-) diffs (71 lines): 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 @@ -120,6 +120,10 @@ ngx_http_index_handler(ngx_http_request_ ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (ilcf->indices == NULL) { + return NGX_DECLINED; + } + allocated = 0; root = 0; dir_tested = 0; @@ -394,7 +398,7 @@ ngx_http_index_create_loc_conf(ngx_conf_ return NULL; } - conf->indices = NULL; + conf->indices = NGX_CONF_UNSET_PTR; conf->max_index_len = 0; return conf; @@ -409,12 +413,12 @@ ngx_http_index_merge_loc_conf(ngx_conf_t ngx_http_index_t *index; - if (conf->indices == NULL) { + if (conf->indices == NGX_CONF_UNSET_PTR) { conf->indices = prev->indices; conf->max_index_len = prev->max_index_len; } - if (conf->indices == NULL) { + if (conf->indices == NGX_CONF_UNSET_PTR) { conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t)); if (conf->indices == NULL) { return NGX_CONF_ERROR; @@ -470,15 +474,30 @@ ngx_http_index_set_index(ngx_conf_t *cf, ngx_http_index_t *index; ngx_http_script_compile_t sc; + value = cf->args->elts; + + if (cf->args->nelts == 2 + && ngx_strcmp(value[1].data, "off") == 0) + { + if (ilcf->indices != NGX_CONF_UNSET_PTR) { + return "is duplicate"; + } + + ilcf->indices = NULL; + return NGX_CONF_OK; + } + if (ilcf->indices == NULL) { + return "is duplicate"; + } + + if (ilcf->indices == NGX_CONF_UNSET_PTR) { ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t)); if (ilcf->indices == NULL) { return NGX_CONF_ERROR; } } - value = cf->args->elts; - for (i = 1; i < cf->args->nelts; i++) { if (value[i].data[0] == '/' && i != cf->args->nelts - 1) { From mdounin at mdounin.ru Tue May 12 22:16:27 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Wed, 13 May 2026 01:16:27 +0300 Subject: [nginx] Index: fixed error message about empty index. Message-ID: details: http://freenginx.org/hg/nginx/rev/207b425c3dba branches: changeset: 9514:207b425c3dba user: Maxim Dounin date: Wed May 13 01:15:32 2026 +0300 description: Index: fixed error message about empty index. diffstat: src/http/modules/ngx_http_index_module.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): 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 @@ -509,7 +509,7 @@ ngx_http_index_set_index(ngx_conf_t *cf, if (value[i].len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "index \"%V\" in \"index\" directive is invalid", - &value[1]); + &value[i]); return NGX_CONF_ERROR; } From mdounin at mdounin.ru Tue May 12 22:16:34 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 13 May 2026 01:16:34 +0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hello! On Mon, May 11, 2026 at 01:54:16PM -0300, Fabiano Furtado wrote: > Hi! > > On Sat, May 9, 2026 at 8:15?PM Maxim Dounin wrote: > > > > Hello! > > [...] > > Looking more into this, I think about only checking "off" if there > > is only one argument. Resulting syntax would be: > > > > index off | file ...; > > > > With this approach, > > > > index off; > > > > will switch off index usage, and will reject any attempts to define > > additional indexes, so > > > > index off; > > index index.html; > > > > and > > > > index index.html; > > index off; > > > > will be rejected. At the same time, when "index off;" is clearly > > not the intention due to multiple arguments of the directive, such > > as in > > > > index index.html off on; > > > > or > > > > index off index.html; > > > > all arguments would be considered index file names, exactly as it > > works now. > > > > I tend to think this would be clearer from the syntax point of > > view. > > > > (The patch is essentially the same, but without checking for "off" > > in the loop.) > > > > What do you think? > > Removing the "off" check from the loop makes the index configuration > more flexible because it allows using "off" as an index file, although > this is an extremely rare use case, which I think is OK. ;) Using "off" as an index file is certainly possible with both variants, it just needs to be written as "./off" in some cases (always with the loop checks, and when it's the only argument otherwise). So I believe the main difference here is how well the behaviour matches user expectations, most notably based on the syntax provided. Committed, thanks for prodding this. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue May 12 23:09:15 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 13 May 2026 02:09:15 +0300 Subject: [PATCH] Documented "index off;" Message-ID: # HG changeset patch # User Maxim Dounin # Date 1778626897 -10800 # Wed May 13 02:01:37 2026 +0300 # Node ID f104c00a4052cffc9a1ea4b9b090b2919dcfb85e # Parent ba01a18ef6e818a52ee7348334e1d510facfed5a Documented "index off;". diff --git a/xml/en/docs/http/ngx_http_index_module.xml b/xml/en/docs/http/ngx_http_index_module.xml --- a/xml/en/docs/http/ngx_http_index_module.xml +++ b/xml/en/docs/http/ngx_http_index_module.xml @@ -43,7 +43,7 @@ location / {
-file ... +file ... | off index.html http server @@ -54,6 +54,7 @@ Defines files that will be used as an in The file name can contain variables. Files are checked in the specified order. The last element of the list can be a file with an absolute path. +The off parameter (1.31.1) disables use of index files. Example: index index.$geo.html index.0.html /index.html; diff --git a/xml/ru/docs/http/ngx_http_index_module.xml b/xml/ru/docs/http/ngx_http_index_module.xml --- a/xml/ru/docs/http/ngx_http_index_module.xml +++ b/xml/ru/docs/http/ngx_http_index_module.xml @@ -42,7 +42,7 @@ location / {
-???? ... +???? ... | off index.html http server @@ -53,6 +53,8 @@ location / { ? ????? ????? ????? ???????????? ??????????. ??????? ?????? ??????????? ? ??????? ?? ????????????. ? ????? ?????? ????? ?????? ???? ? ?????????? ?????. +???????? off (1.31.1) ????????? ????????????? +????????? ??????. ??????: index index.$geo.html index.0.html /index.html; From dpfitzner at netflix.com Wed May 13 06:02:32 2026 From: dpfitzner at netflix.com (David Pfitzner) Date: Wed, 13 May 2026 15:32:32 +0930 Subject: One more unsafe ngx_errno usage Message-ID: Hi, A while ago there was this commit: https://freenginx.org/hg/nginx/rev/d596a1fb8b9c "Cleaned up unsafe ngx_errno and ngx_socket_errno usage." I just noticed that one part of the above fix was incomplete. In src/os/unix/ngx_freebsd_init.c (tip) starting at line 125 we have: if (sysctlbyname("kern.osrelease", ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { err = ngx_errno; ngx_log_error(NGX_LOG_ALERT, log, err, "sysctlbyname(kern.osrelease) failed"); if (ngx_errno != NGX_ENOMEM) { return NGX_ERROR; } In the 'if' condition at the end, I think 'ngx_errno' should instead be 'err' (similar to the code block just previous). Sorry, I didn't create a patch, but the fix is simple. Regards, David -------------- next part -------------- An HTML attachment was scrubbed... URL: From osa at freebsd.org.ru Wed May 13 11:14:02 2026 From: osa at freebsd.org.ru (Sergey A. Osokin) Date: Wed, 13 May 2026 14:14:02 +0300 Subject: One more unsafe ngx_errno usage In-Reply-To: References: Message-ID: Hi David, On Wed, May 13, 2026 at 03:32:32PM +0930, David Pfitzner via nginx-devel wrote: > > A while ago there was this commit: > https://freenginx.org/hg/nginx/rev/d596a1fb8b9c > "Cleaned up unsafe ngx_errno and ngx_socket_errno usage." > > I just noticed that one part of the above fix was incomplete. In > src/os/unix/ngx_freebsd_init.c (tip) starting at line 125 we have: > > if (sysctlbyname("kern.osrelease", > ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { > err = ngx_errno; > > ngx_log_error(NGX_LOG_ALERT, log, err, > "sysctlbyname(kern.osrelease) failed"); > > if (ngx_errno != NGX_ENOMEM) { > return NGX_ERROR; > } > > In the 'if' condition at the end, I think 'ngx_errno' should instead be > 'err' (similar to the code block just previous). Sorry, I didn't create a > patch, but the fix is simple. It seems to me that's related to the following line, https://freenginx.org/hg/nginx/rev/d596a1fb8b9c#l10.28 - if (ngx_errno != NGX_ENOMEM) { + if (err != NGX_ENOMEM) { Am I right? Thank you. -- Sergey A. Osokin From fabianofurtado at gmail.com Wed May 13 13:00:29 2026 From: fabianofurtado at gmail.com (Fabiano Furtado) Date: Wed, 13 May 2026 10:00:29 -0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hi! On Tue, May 12, 2026 at 7:24?PM Maxim Dounin wrote: > > Hello! > > [...] > > Using "off" as an index file is certainly possible with both > variants, it just needs to be written as "./off" in some cases > (always with the loop checks, and when it's the only argument > otherwise). OK! > So I believe the main difference here is how well the behaviour > matches user expectations, most notably based on the syntax > provided. Agreed! > Committed, thanks for prodding this. Thank you for accepting my idea and developing the patch! This is my first contribution to open-source software! :) Yet, I have one doubt, specifically at the end of the function ngx_http_index_merge_loc_conf(). [...] conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); return NGX_CONF_OK; /* Line 435 */ } return NGX_CONF_OK; /* Line 438 */ } There are two "return" statements with the same parameter: lines 435 and 438. At line 435, do we really need this "return" statement, or is it required to comply with the guidelines from https://freenginx.org/en/docs/contributing_changes.html ? Thanks in advance. Fabiano Furtado From mdounin at mdounin.ru Wed May 13 13:21:43 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 13 May 2026 16:21:43 +0300 Subject: One more unsafe ngx_errno usage In-Reply-To: References: Message-ID: Hello! On Wed, May 13, 2026 at 03:32:32PM +0930, David Pfitzner via nginx-devel wrote: > A while ago there was this commit: > https://freenginx.org/hg/nginx/rev/d596a1fb8b9c > "Cleaned up unsafe ngx_errno and ngx_socket_errno usage." > > I just noticed that one part of the above fix was incomplete. In > src/os/unix/ngx_freebsd_init.c (tip) starting at line 125 we have: > > if (sysctlbyname("kern.osrelease", > ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { > err = ngx_errno; > > ngx_log_error(NGX_LOG_ALERT, log, err, > "sysctlbyname(kern.osrelease) failed"); > > if (ngx_errno != NGX_ENOMEM) { > return NGX_ERROR; > } > > In the 'if' condition at the end, I think 'ngx_errno' should instead be > 'err' (similar to the code block just previous). Sorry, I didn't create a > patch, but the fix is simple. Sure, thanks for catching this. Just in case, the patch is below: # HG changeset patch # User Maxim Dounin # Date 1778678442 -10800 # Wed May 13 16:20:42 2026 +0300 # Node ID 5fec4281a4931de7888a7994ff1bd7f6d7254fb7 # Parent 207b425c3dba591ba7dcd9e5533ae20bb0e0caed Fixed unsafe ngx_errno usage, missed in 9457:d596a1fb8b9c. Reported by David Pfitzner. diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -129,7 +129,7 @@ ngx_os_specific_init(ngx_log_t *log) ngx_log_error(NGX_LOG_ALERT, log, err, "sysctlbyname(kern.osrelease) failed"); - if (ngx_errno != NGX_ENOMEM) { + if (err != NGX_ENOMEM) { return NGX_ERROR; } -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Wed May 13 14:05:15 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Wed, 13 May 2026 17:05:15 +0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hello! On Wed, May 13, 2026 at 10:00:29AM -0300, Fabiano Furtado wrote: > On Tue, May 12, 2026 at 7:24?PM Maxim Dounin wrote: > > > > Hello! > > > > [...] > > > > Using "off" as an index file is certainly possible with both > > variants, it just needs to be written as "./off" in some cases > > (always with the loop checks, and when it's the only argument > > otherwise). > > OK! > > > So I believe the main difference here is how well the behaviour > > matches user expectations, most notably based on the syntax > > provided. > > Agreed! > > > Committed, thanks for prodding this. > > Thank you for accepting my idea and developing the patch! This is my > first contribution to open-source software! :) Congratulations! :) > Yet, I have one doubt, specifically at the end of the function > ngx_http_index_merge_loc_conf(). > > [...] > conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX); > > return NGX_CONF_OK; /* Line 435 */ > } > > return NGX_CONF_OK; /* Line 438 */ > } > > There are two "return" statements with the same parameter: lines 435 and 438. > > At line 435, do we really need this "return" statement, or is it > required to comply with the guidelines from > https://freenginx.org/en/docs/contributing_changes.html ? Certainly the first return is not needed here, at least with the existing code. No coding style rules require two returns here, but they still can appear for various reasons - for example, when two distinct returns are better from readability point of view, notably when some additional code might appear after the "if" block and this code is not expected to be executed when "if" matches. My best guess is that it is a leftover from the previously used code structure, where there was some proof-of-concept additional code after the "no indices defined" if block (and this code wasn't expected to be executed without user-defined indices): https://freenginx.org/hg/nginx/rev/9b8c906f6e63#l63.677 Still, it might be intentionally left this way (e.g., assuming that some code for user-defined indices will be added later). -- Maxim Dounin http://mdounin.ru/ From fabianofurtado at gmail.com Wed May 13 15:37:50 2026 From: fabianofurtado at gmail.com (Fabiano Furtado) Date: Wed, 13 May 2026 12:37:50 -0300 Subject: "index off;" directive? In-Reply-To: References: Message-ID: Hello! On Wed, May 13, 2026 at 11:05?AM Maxim Dounin wrote: > > Hello! > [...] > > Thank you for accepting my idea and developing the patch! This is my > > first contribution to open-source software! :) > > Congratulations! :) > > > Yet, I have one doubt, specifically at the end of the function > > ngx_http_index_merge_loc_conf(). > > > > [...] > Certainly the first return is not needed here, at least with the > existing code. No coding style rules require two returns here, > but they still can appear for various reasons - for example, when > two distinct returns are better from readability point of view, > notably when some additional code might appear after the "if" block > and this code is not expected to be executed when "if" matches. > > My best guess is that it is a leftover from the previously used > code structure, where there was some proof-of-concept additional > code after the "no indices defined" if block (and this code wasn't > expected to be executed without user-defined indices): > > https://freenginx.org/hg/nginx/rev/9b8c906f6e63#l63.677 > > Still, it might be intentionally left this way (e.g., assuming > that some code for user-defined indices will be added later). OK. Thank you! From dpfitzner at netflix.com Thu May 14 00:48:25 2026 From: dpfitzner at netflix.com (David Pfitzner) Date: Thu, 14 May 2026 10:18:25 +0930 Subject: One more unsafe ngx_errno usage In-Reply-To: References: Message-ID: On Wed, May 13, 2026 at 10:51?PM Maxim Dounin wrote: > Hello! > > On Wed, May 13, 2026 at 03:32:32PM +0930, David Pfitzner via nginx-devel > wrote: > > > A while ago there was this commit: > > https://freenginx.org/hg/nginx/rev/d596a1fb8b9c > > "Cleaned up unsafe ngx_errno and ngx_socket_errno usage." > > > > I just noticed that one part of the above fix was incomplete. In > > src/os/unix/ngx_freebsd_init.c (tip) starting at line 125 we have: > > > > if (sysctlbyname("kern.osrelease", > > ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { > > err = ngx_errno; > > > > ngx_log_error(NGX_LOG_ALERT, log, err, > > "sysctlbyname(kern.osrelease) failed"); > > > > if (ngx_errno != NGX_ENOMEM) { > > return NGX_ERROR; > > } > > > > In the 'if' condition at the end, I think 'ngx_errno' should instead be > > 'err' (similar to the code block just previous). Sorry, I didn't create a > > patch, but the fix is simple. > > Sure, thanks for catching this. > Just in case, the patch is below: > > # HG changeset patch > # User Maxim Dounin > # Date 1778678442 -10800 > # Wed May 13 16:20:42 2026 +0300 > # Node ID 5fec4281a4931de7888a7994ff1bd7f6d7254fb7 > # Parent 207b425c3dba591ba7dcd9e5533ae20bb0e0caed > Fixed unsafe ngx_errno usage, missed in 9457:d596a1fb8b9c. > > Reported by David Pfitzner. > > diff --git a/src/os/unix/ngx_freebsd_init.c > b/src/os/unix/ngx_freebsd_init.c > --- a/src/os/unix/ngx_freebsd_init.c > +++ b/src/os/unix/ngx_freebsd_init.c > @@ -129,7 +129,7 @@ ngx_os_specific_init(ngx_log_t *log) > ngx_log_error(NGX_LOG_ALERT, log, err, > "sysctlbyname(kern.osrelease) failed"); > > - if (ngx_errno != NGX_ENOMEM) { > + if (err != NGX_ENOMEM) { > return NGX_ERROR; > } > > > > Yes, that patch looks good, thanks! -- David -------------- next part -------------- An HTML attachment was scrubbed... URL: From mdounin at mdounin.ru Fri May 15 04:05:32 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Fri, 15 May 2026 07:05:32 +0300 Subject: [nginx] Fixed unsafe ngx_errno usage, missed in 9457:d596a1fb8b9c. Message-ID: details: http://freenginx.org/hg/nginx/rev/5fec4281a493 branches: changeset: 9515:5fec4281a493 user: Maxim Dounin date: Wed May 13 16:20:42 2026 +0300 description: Fixed unsafe ngx_errno usage, missed in 9457:d596a1fb8b9c. Reported by David Pfitzner. diffstat: src/os/unix/ngx_freebsd_init.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/src/os/unix/ngx_freebsd_init.c b/src/os/unix/ngx_freebsd_init.c --- a/src/os/unix/ngx_freebsd_init.c +++ b/src/os/unix/ngx_freebsd_init.c @@ -129,7 +129,7 @@ ngx_os_specific_init(ngx_log_t *log) ngx_log_error(NGX_LOG_ALERT, log, err, "sysctlbyname(kern.osrelease) failed"); - if (ngx_errno != NGX_ENOMEM) { + if (err != NGX_ENOMEM) { return NGX_ERROR; } From mdounin at mdounin.ru Fri May 15 04:06:33 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Fri, 15 May 2026 07:06:33 +0300 Subject: One more unsafe ngx_errno usage In-Reply-To: References: Message-ID: Hello! On Thu, May 14, 2026 at 10:18:25AM +0930, David Pfitzner via nginx-devel wrote: > On Wed, May 13, 2026 at 10:51?PM Maxim Dounin wrote: > > > Hello! > > > > On Wed, May 13, 2026 at 03:32:32PM +0930, David Pfitzner via nginx-devel > > wrote: > > > > > A while ago there was this commit: > > > https://freenginx.org/hg/nginx/rev/d596a1fb8b9c > > > "Cleaned up unsafe ngx_errno and ngx_socket_errno usage." > > > > > > I just noticed that one part of the above fix was incomplete. In > > > src/os/unix/ngx_freebsd_init.c (tip) starting at line 125 we have: > > > > > > if (sysctlbyname("kern.osrelease", > > > ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) { > > > err = ngx_errno; > > > > > > ngx_log_error(NGX_LOG_ALERT, log, err, > > > "sysctlbyname(kern.osrelease) failed"); > > > > > > if (ngx_errno != NGX_ENOMEM) { > > > return NGX_ERROR; > > > } > > > > > > In the 'if' condition at the end, I think 'ngx_errno' should instead be > > > 'err' (similar to the code block just previous). Sorry, I didn't create a > > > patch, but the fix is simple. > > > > Sure, thanks for catching this. > > Just in case, the patch is below: > > > > # HG changeset patch > > # User Maxim Dounin > > # Date 1778678442 -10800 > > # Wed May 13 16:20:42 2026 +0300 > > # Node ID 5fec4281a4931de7888a7994ff1bd7f6d7254fb7 > > # Parent 207b425c3dba591ba7dcd9e5533ae20bb0e0caed > > Fixed unsafe ngx_errno usage, missed in 9457:d596a1fb8b9c. > > > > Reported by David Pfitzner. > > > > diff --git a/src/os/unix/ngx_freebsd_init.c > > b/src/os/unix/ngx_freebsd_init.c > > --- a/src/os/unix/ngx_freebsd_init.c > > +++ b/src/os/unix/ngx_freebsd_init.c > > @@ -129,7 +129,7 @@ ngx_os_specific_init(ngx_log_t *log) > > ngx_log_error(NGX_LOG_ALERT, log, err, > > "sysctlbyname(kern.osrelease) failed"); > > > > - if (ngx_errno != NGX_ENOMEM) { > > + if (err != NGX_ENOMEM) { > > return NGX_ERROR; > > } > > > > > > > > Yes, that patch looks good, thanks! Committed, thanks again. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Sun May 17 00:12:34 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:34 +0300 Subject: [PATCH 01 of 10] Rewrite: fixed incorrect escaping and possible segfault Message-ID: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778971812 -10800 # Sun May 17 01:50:12 2026 +0300 # Node ID 9aadc356492de0e61c8ee202c246c2353e8fbc83 # Parent 5fec4281a4931de7888a7994ff1bd7f6d7254fb7 Rewrite: fixed incorrect escaping and possible segfault. Similarly to 4617:972642646f06, the following code resulted in incorrect escaping of the $temp variable and possible segfault: location / { rewrite ^(.*) /uri?args; set $temp $1; return 200 "$temp"; } If there were arguments in rewrite's replacement string, the is_args flag was set and never cleared. This resulted in escaping being incorrectly applied to positional captures evaluated after the rewrite in the same script engine, notably in "set", "if", and "rewrite" directives. Additionally, in "set", "if", and "rewrite" with duplicate captures or additional variables, the buffer was allocated without escaping expected, so this also resulted in a buffer overrun and a possible segfault (CVE-2026-42945). The fix is to clear the is_args flag after rewrite evaluation in ngx_http_script_regex_end_code(), similarly to how we clear e->quote and e->args. Additionally, to ensure that buffer allocation stays correct even if the is_args flag is somehow set, e->is_args is now propagated to length calculations in ngx_http_script_regex_start_code() and in ngx_http_script_complex_value_code(). See also: https://github.com/nginx/nginx/commit/2046b45aa0c6e712c216b9075886f3f26e9b4ca9 diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1161,6 +1161,7 @@ ngx_http_script_regex_start_code(ngx_htt le.line = e->line; le.request = r; le.quote = code->redirect; + le.is_args = e->is_args; len = 0; @@ -1203,6 +1204,7 @@ ngx_http_script_regex_end_code(ngx_http_ r = e->request; e->quote = 0; + e->is_args = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http script regex end"); @@ -1769,6 +1771,7 @@ ngx_http_script_complex_value_code(ngx_h le.line = e->line; le.request = e->request; le.quote = e->quote; + le.is_args = e->is_args; for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; From mdounin at mdounin.ru Sun May 17 00:12:35 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:35 +0300 Subject: [PATCH 02 of 10] Rewrite: fixed incorrect initial escaping in "set" In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <071c2e847b986bb7b133.1778976755@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778973194 -10800 # Sun May 17 02:13:14 2026 +0300 # Node ID 071c2e847b986bb7b133ff42c26f9aa2f55b102b # Parent 9aadc356492de0e61c8ee202c246c2353e8fbc83 Rewrite: fixed incorrect initial escaping in "set". Previously, the following configuration resulted in inconsistent escaping of the $temp variable in requests to "/a/..." and "/b/...": map $uri $foo { ~(.*) 1; } location /a/ { set $temp $foo:$1; return 200 $temp; } location /b/ { rewrite ^(.*) $1; set $temp $foo:$1; return 200 $temp; } In requests to "/a/..." the $1 capture contents were escaped when copying to the $temp variable due to e->quote being initially set in ngx_http_rewrite_handler(). And in requests to "/b/..." escaping was not used, since e->quote was cleared by ngx_http_script_regex_start_code() and ngx_http_script_regex_end_code(). Note well that in both cases $1 comes from a regular expression in the map, and different behaviour here is clearly unexpected. The "e->quote = 1;" initialization in ngx_http_rewrite_handler() was originally introduced in 485:4ebe09b07e30 (0.1.17) along with the new rewrite module, and had no effect at the time: e->quote was always set before being used. It started to affect the module behaviour after the introduction of the "set" directive in 501:d4ea69372b94 (0.1.25) and variables support in "set" in 515:417a087c9c4d (0.1.32). With this change, e->quote is no longer set in ngx_http_rewrite_handler(), resulting in consistent behaviour in all cases (and also matching the behaviour seen without an intermediate "set"). diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -171,7 +171,6 @@ ngx_http_rewrite_handler(ngx_http_reques e->ip = rlcf->codes->elts; e->request = r; - e->quote = 1; e->log = rlcf->log; e->status = NGX_DECLINED; From mdounin at mdounin.ru Sun May 17 00:12:36 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:36 +0300 Subject: [PATCH 03 of 10] Charset: fixed handling of incomplete UTF-8 characters In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <52d992b6a7d5e7910761.1778976756@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778974119 -10800 # Sun May 17 02:28:39 2026 +0300 # Node ID 52d992b6a7d5e79107616f948fed273a69fb7d53 # Parent 071c2e847b986bb7b133ff42c26f9aa2f55b102b Charset: fixed handling of incomplete UTF-8 characters. Previously, if a UTF-8 character was split across multiple buffers, the second and subsequent buffers were handled incorrectly: ngx_decode_utf8() was called with the wrong size if there are fewer bytes in the buffer than ctx->saved can hold, the following code called ngx_memcpy() with the wrong size, potentially reading past the supplied buffer, and ctx->saved_len was set to an incorrect value, which could later result in reading before the buffer (CVE-2026-42934). The fix is to adjust the code to make sure that the "i" value properly represents the number of bytes available in ctx->saved in all cases, remove the unneeded ngx_memcpy() call, and set ctx->saved_len to the correct value. See also: https://github.com/nginx/nginx/commit/696a7f1b9198d576e6a59c1655b746fbf06561cf diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -788,8 +788,8 @@ ngx_http_charset_recode_from_utf8(ngx_po p = src; - for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) { - ctx->saved[i] = *p++; + for (i = ctx->saved_len; i < NGX_UTF_LEN; /* void */) { + ctx->saved[i++] = *p++; if (p == buf->last) { break; @@ -826,8 +826,7 @@ ngx_http_charset_recode_from_utf8(ngx_po b->sync = 1; b->shadow = buf; - ngx_memcpy(&ctx->saved[ctx->saved_len], src, i); - ctx->saved_len += i; + ctx->saved_len = i; return out; } From mdounin at mdounin.ru Sun May 17 00:12:37 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:37 +0300 Subject: [PATCH 04 of 10] Charset: fixed reuse of buffers without associated memory In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <7dca2cab958f44f580af.1778976757@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778975065 -10800 # Sun May 17 02:44:25 2026 +0300 # Node ID 7dca2cab958f44f580af78bef97bc0037032d7f4 # Parent 52d992b6a7d5e79107616f948fed273a69fb7d53 Charset: fixed reuse of buffers without associated memory. Previously, in ngx_http_charset_body_filter() buffers with b->pos set were assumed to have an associated memory allocation and were saved to the ctx->free_buffers list. However, b->pos is set in all buffers, including ones without associated memory. This was safe, since such buffers were ignored by ngx_http_charset_get_buffer(), yet reuse of such buffers via ngx_http_charset_get_buf() was broken. The fix is to check b->start instead in ngx_http_charset_body_filter(). To make sure cached buffers can actually be used, various internal fields are now properly cleared when using cached buffers. Also, b->sync is now properly cleared in cached buffers returned by ngx_http_charset_get_buffer(). And b->pos of the known-to-be-empty sync buffer allocated in ngx_http_charset_recode_from_utf8() is set to b->last, to avoid unexpected sizes shown in logs. diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -630,7 +630,7 @@ ngx_http_charset_body_filter(ngx_http_re b->shadow->pos = b->shadow->last; } - if (b->pos) { + if (b->start) { cl->next = ctx->free_buffers; ctx->free_buffers = cl; continue; @@ -821,7 +821,7 @@ ngx_http_charset_recode_from_utf8(ngx_po b = out->buf; - b->pos = buf->pos; + b->pos = buf->last; b->last = buf->last; b->sync = 1; b->shadow = buf; @@ -1094,15 +1094,23 @@ recode: static ngx_chain_t * ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx) { + ngx_buf_t *b; ngx_chain_t *cl; cl = ctx->free_bufs; if (cl) { ctx->free_bufs = cl->next; + cl->next = NULL; - cl->buf->shadow = NULL; - cl->next = NULL; + b = cl->buf; + + b->temporary = 0; + b->memory = 0; + b->mmap = 0; + b->flush = 0; + b->sync = 0; + b->shadow = NULL; return cl; } @@ -1144,6 +1152,7 @@ ngx_http_charset_get_buffer(ngx_pool_t * b->pos = b->start; b->temporary = 1; + b->sync = 0; b->shadow = NULL; return cl; From mdounin at mdounin.ru Sun May 17 00:12:38 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:38 +0300 Subject: [PATCH 05 of 10] Charset: improved charset_map parsing In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1778975841 -10800 # Sun May 17 02:57:21 2026 +0300 # Node ID fa0584dce8b52ec59233714a695cc5966b9a16da # Parent 7dca2cab958f44f580af78bef97bc0037032d7f4 Charset: improved charset_map parsing. Previously, too long UTF-8 codes in charset_map resulted in writing outside of the allocated src2dst buffer, potentially causing a segmentation fault when parsing an invalid configuration. Also, the "dst > 255" check is unnecessary when parsing exactly two hex characters, and therefore was removed. See also: https://github.com/nginx/nginx/commit/a813c639211728a1441945dee149b44a0935f48b diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -1345,11 +1345,17 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx if (ctx->charset->utf8) { p = &table->src2dst[src * NGX_UTF_LEN]; + if (value[1].len / 2 > NGX_UTF_LEN - 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + *p++ = (u_char) (value[1].len / 2); for (i = 0; i < value[1].len; i += 2) { dst = ngx_hextoi(&value[1].data[i], 2); - if (dst == NGX_ERROR || dst > 255) { + if (dst == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[1]); return NGX_CONF_ERROR; From mdounin at mdounin.ru Sun May 17 00:12:39 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:39 +0300 Subject: [PATCH 06 of 10] Updated copyright year for upcoming patch imports In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <01b1469ce96ce2927976.1778976759@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1778976279 -10800 # Sun May 17 03:04:39 2026 +0300 # Node ID 01b1469ce96ce2927976ba4b9e9a532ae39de462 # Parent fa0584dce8b52ec59233714a695cc5966b9a16da Updated copyright year for upcoming patch imports. diff --git a/docs/text/LICENSE b/docs/text/LICENSE --- a/docs/text/LICENSE +++ b/docs/text/LICENSE @@ -1,6 +1,6 @@ /* * Copyright (C) 2002-2021 Igor Sysoev - * Copyright (C) 2011-2024 Nginx, Inc. + * Copyright (C) 2011-2026 Nginx, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without From mdounin at mdounin.ru Sun May 17 00:12:40 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:40 +0300 Subject: [PATCH 07 of 10] From 71841dcedfdf46048ef5e25413fdf97a66957913 Mon Sep 17 00:00:00 2001 In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <76599662afacdc5732fe.1778976760@vm-bsd.mdounin.ru> # HG changeset patch # User Roman Arutyunyan # Date 1776768701 -14400 # Tue Apr 21 14:51:41 2026 +0400 # Node ID 76599662afacdc5732fe33583cc7ed02716701a5 # Parent 01b1469ce96ce2927976ba4b9e9a532ae39de462 >From 71841dcedfdf46048ef5e25413fdf97a66957913 Mon Sep 17 00:00:00 2001 OCSP: resolve cleanup on connection close. Previously, when a client SSL connection was terminated (typically due to a timeout) while resolving an OCSP responder, the OCSP context was freed, but the resolve context was not. This resulted in use-after-free on resolve completion. Reported by Leo Lin. Obtained from: https://github.com/nginx/nginx/commit/71841dcedfdf46048ef5e25413fdf97a66957913 diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -111,6 +111,7 @@ struct ngx_ssl_ocsp_ctx_s { ngx_resolver_t *resolver; ngx_msec_t resolver_timeout; + ngx_resolver_ctx_t *resolve; ngx_msec_t timeout; @@ -1303,6 +1304,10 @@ ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ct ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp done"); + if (ctx->resolve) { + ngx_resolve_name_done(ctx->resolve); + } + if (ctx->peer.connection) { ngx_close_connection(ctx->peer.connection); } @@ -1395,7 +1400,10 @@ ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t resolve->data = ctx; resolve->timeout = ctx->resolver_timeout; + ctx->resolve = resolve; + if (ngx_resolve_name(resolve) != NGX_OK) { + ctx->resolve = NULL; ngx_ssl_ocsp_error(ctx); return; } @@ -1484,6 +1492,7 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolve } ngx_resolve_name_done(resolve); + ctx->resolve = NULL; ngx_ssl_ocsp_connect(ctx); return; @@ -1491,6 +1500,8 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolve failed: ngx_resolve_name_done(resolve); + ctx->resolve = NULL; + ngx_ssl_ocsp_error(ctx); } From mdounin at mdounin.ru Sun May 17 00:12:41 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:41 +0300 Subject: [PATCH 08 of 10] QUIC: avoid assigning unvalidated address to new streams In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: <4f8c88f32b9723c688fd.1778976761@vm-bsd.mdounin.ru> # HG changeset patch # User Roman Arutyunyan # Date 1777554953 -14400 # Thu Apr 30 17:15:53 2026 +0400 # Node ID 4f8c88f32b9723c688fd76c121f8f39361967097 # Parent 76599662afacdc5732fe33583cc7ed02716701a5 QUIC: avoid assigning unvalidated address to new streams. Previously, when a client migrated to a new address, new QUIC streams received this address before validation. This allowed an attacker to create QUIC streams with a spoofed address. Reported by Rodrigo Laneth. Obtained from: https://github.com/nginx/nginx/commit/f37ec3e5d4f527e52ed5b25951ad8aa7d1ff6266 diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -193,6 +193,8 @@ valid: path->validated = 1; + ngx_quic_set_connection_path(c, path); + if (path->mtu_unvalidated) { path->mtu_unvalidated = 0; return ngx_quic_validate_path(c, path); @@ -510,9 +512,10 @@ ngx_quic_handle_migration(ngx_connection qc->path = next; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, next); + if (next->validated) { + ngx_quic_set_connection_path(c, next); - if (!next->validated && next->state != NGX_QUIC_PATH_VALIDATING) { + } else if (next->state != NGX_QUIC_PATH_VALIDATING) { if (ngx_quic_validate_path(c, next) != NGX_OK) { return NGX_ERROR; } @@ -806,8 +809,6 @@ ngx_quic_expire_path_validation(ngx_conn qc->path = bkp; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, qc->path); - ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic path seq:%uL addr:%V is restored from backup", qc->path->seqnum, &qc->path->addr_text); From mdounin at mdounin.ru Sun May 17 00:12:42 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:42 +0300 Subject: [PATCH 09 of 10] From f79c286b34d3b708bd4856a56e27529e11386098 Mon Sep 17 00:00:00 2001 In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Sergey Kandaurov # Date 1777485411 -14400 # Wed Apr 29 21:56:51 2026 +0400 # Node ID f4515f0ba7bcf8f0c7ed6e4e2024c051b39c31f1 # Parent 4f8c88f32b9723c688fd76c121f8f39361967097 >From f79c286b34d3b708bd4856a56e27529e11386098 Mon Sep 17 00:00:00 2001 Upstream: reset parsing state after invalid status line. Previously, it was possible to start parsing headers with a wrong parsing state after status line was not recognized, as a fallback used in the scgi and uwsgi modules. Reported by Leo Lin. Obtained from: https://github.com/nginx/nginx/commit/f79c286b34d3b708bd4856a56e27529e11386098 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 @@ -1016,6 +1016,7 @@ ngx_http_scgi_process_status_line(ngx_ht if (rc == NGX_ERROR) { u->process_header = ngx_http_scgi_process_header; + r->state = 0; return ngx_http_scgi_process_header(r); } 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 @@ -1245,6 +1245,7 @@ ngx_http_uwsgi_process_status_line(ngx_h if (rc == NGX_ERROR) { u->process_header = ngx_http_uwsgi_process_header; + r->state = 0; return ngx_http_uwsgi_process_header(r); } From mdounin at mdounin.ru Sun May 17 00:12:43 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 17 May 2026 03:12:43 +0300 Subject: [PATCH 10 of 10] Upstream: fixed parsing of split status lines In-Reply-To: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Sergey Kandaurov # Date 1777489340 -14400 # Wed Apr 29 23:02:20 2026 +0400 # Node ID b454cd489cfd4e42ba3d45e0f34587bedd6cd463 # Parent f4515f0ba7bcf8f0c7ed6e4e2024c051b39c31f1 Upstream: fixed parsing of split status lines. If the first response line was split across reads and it didn't appear a status line, the portion already processed was lost. The change introduces a new field for proper backtracking on status line fallback. Obtained from (with minor changes): https://github.com/nginx/nginx/commit/5f86648ef8c969e98aa2f7b938472296b12055be 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 @@ -1862,6 +1862,8 @@ ngx_http_proxy_process_status_line(ngx_h u->headers_in.status_n = 200; u->headers_in.connection_close = 1; + u->buffer.pos = ctx->status.line_start; + return NGX_OK; } 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 @@ -1016,6 +1016,7 @@ ngx_http_scgi_process_status_line(ngx_ht if (rc == NGX_ERROR) { u->process_header = ngx_http_scgi_process_header; + u->buffer.pos = status->line_start; r->state = 0; return ngx_http_scgi_process_header(r); } 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 @@ -1245,6 +1245,7 @@ ngx_http_uwsgi_process_status_line(ngx_h if (rc == NGX_ERROR) { u->process_header = ngx_http_uwsgi_process_header; + u->buffer.pos = status->line_start; r->state = 0; return ngx_http_uwsgi_process_header(r); } diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -72,6 +72,7 @@ struct ngx_http_chunked_s { typedef struct { ngx_uint_t http_version; ngx_uint_t code; + u_char *line_start; u_char *start; u_char *end; } ngx_http_status_t; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1663,6 +1663,8 @@ ngx_http_parse_status_line(ngx_http_requ /* "HTTP/" */ case sw_start: + status->line_start = p; + switch (ch) { case 'H': state = sw_H; From mdounin at mdounin.ru Sun May 17 00:19:13 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Sun, 17 May 2026 03:19:13 +0300 Subject: [PATCH 07 of 10] From 71841dcedfdf46048ef5e25413fdf97a66957913 Mon Sep 17 00:00:00 2001 In-Reply-To: <76599662afacdc5732fe.1778976760@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> <76599662afacdc5732fe.1778976760@vm-bsd.mdounin.ru> Message-ID: Hello! On Sun, May 17, 2026 at 03:12:40AM +0300, Maxim Dounin wrote: [...] > From 71841dcedfdf46048ef5e25413fdf97a66957913 Mon Sep 17 00:00:00 2001 Err, mq extensions seems to mishandle git patches in various unexpected ways, fixed. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Mon May 18 00:42:51 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 18 May 2026 03:42:51 +0300 Subject: [nginx] SCGI: fixed handling of unbuffered request body. Message-ID: details: http://freenginx.org/hg/nginx/rev/f84effafce4a branches: changeset: 9516:f84effafce4a user: Maxim Dounin date: Mon May 18 03:41:05 2026 +0300 description: SCGI: fixed handling of unbuffered request body. Previously, size of the request body as sent in the CONTENT_LENGTH param was calculated based on the size of the request buffers available. This is, however, wrong for unbuffered request body, since the request body might not be fully available yet. The fix is to use r->headers_in.content_length_n (as long as it is available and there is a body), much like other modules do. See also: https://github.com/nginx/nginx/commit/ec714d52bd4914d52a113234c16e1855d9ac7dcf diffstat: src/http/modules/ngx_http_scgi_module.c | 8 +++----- 1 files changed, 3 insertions(+), 5 deletions(-) diffs (18 lines): 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 @@ -649,11 +649,9 @@ ngx_http_scgi_create_request(ngx_http_re u_char buffer[NGX_OFF_T_LEN]; content_length_n = 0; - body = r->upstream->request_bufs; - - while (body) { - content_length_n += ngx_buf_size(body->buf); - body = body->next; + + if (r->headers_in.content_length_n > 0 && !r->discard_body) { + content_length_n = r->headers_in.content_length_n; } content_length.data = buffer; From mdounin at mdounin.ru Mon May 18 00:43:04 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 18 May 2026 03:43:04 +0300 Subject: [nginx-tests] Tests: improved SCGI request body tests. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/ec886195a369 branches: changeset: 2054:ec886195a369 user: Maxim Dounin date: Mon May 18 03:41:13 2026 +0300 description: Tests: improved SCGI request body tests. diffstat: scgi_body.t | 49 +++++++++++++++++++++++-------------------------- 1 files changed, 23 insertions(+), 26 deletions(-) diffs (82 lines): diff --git a/scgi_body.t b/scgi_body.t --- a/scgi_body.t +++ b/scgi_body.t @@ -2,7 +2,7 @@ # (C) Maxim Dounin -# Test for scgi backend with chunked request body. +# Test for scgi backend with request body. ############################################################################### @@ -57,32 +57,13 @@ EOF ############################################################################### -like(http_get('/'), qr/X-Body: /, 'scgi no body'); - -like(http_get_length('/', ''), qr/X-Body: /, 'scgi empty body'); -like(http_get_length('/', 'foobar'), qr/X-Body: foobar/, 'scgi body'); - -like(http(<connection()->print(< details: http://freenginx.org/hg/nginx-tests/rev/0ca3437b2f32 branches: changeset: 2055:0ca3437b2f32 user: Maxim Dounin date: Mon May 18 03:41:15 2026 +0300 description: Tests: SCGI tests for non-buffered and discarded request body. diffstat: scgi_body.t | 37 ++++++++++++++++++++++++++++++++++++- 1 files changed, 36 insertions(+), 1 deletions(-) diffs (68 lines): diff --git a/scgi_body.t b/scgi_body.t --- a/scgi_body.t +++ b/scgi_body.t @@ -24,7 +24,7 @@ select STDOUT; $| = 1; eval { require SCGI; }; plan(skip_all => 'SCGI not installed') if $@; -my $t = Test::Nginx->new()->has(qw/http scgi/)->plan(5) +my $t = Test::Nginx->new()->has(qw/http scgi/)->plan(7) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -46,6 +46,18 @@ http { scgi_param SCGI 1; scgi_param REQUEST_URI $request_uri; } + + location /nb/ { + scgi_pass 127.0.0.1:8081; + scgi_param SCGI 1; + scgi_param REQUEST_URI $request_uri; + scgi_request_buffering off; + } + + location /discard/ { + client_max_body_size 1; + error_page 413 /error413; + } } } @@ -65,6 +77,17 @@ like(http_get_length('/', ''), qr/X-Body like(http_get_chunked('/', 'foobar'), qr/X-Body: 'foobar'/, 'scgi chunked'); like(http_get_chunked('/', ''), qr/X-Body: ''/, 'scgi empty chunked'); +TODO: { +local $TODO = 'not yet' unless $t->has_version('1.31.1'); + +like(http_get_nobuf('/nb/', 'foo', 'bar'), qr/X-Body: 'foobar'/, + 'scgi no request buffering'); + +} + +like(http_get_nobuf('/discard/', 'foo', 'bar'), qr/X-Body: ''/, + 'scgi discard body'); + ############################################################################### sub http_get_length { @@ -96,6 +119,18 @@ 0 EOF } +sub http_get_nobuf { + my ($url, $body1, $body2) = @_; + my $length = length($body1) + length($body2); + return http(< 0.1, body => $body2); +GET $url HTTP/1.1 +Host: localhost +Connection: close +Content-Length: $length + +EOF +} + ############################################################################### sub scgi_daemon { From mdounin at mdounin.ru Mon May 18 00:43:05 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 18 May 2026 03:43:05 +0300 Subject: [nginx-tests] Tests: uwsgi tests for non-buffered and discarded ... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/dca9f4b8950e branches: changeset: 2056:dca9f4b8950e user: Maxim Dounin date: Mon May 18 03:41:18 2026 +0300 description: Tests: uwsgi tests for non-buffered and discarded request body. diffstat: uwsgi_body.t | 32 +++++++++++++++++++++++++++++++- 1 files changed, 31 insertions(+), 1 deletions(-) diffs (67 lines): diff --git a/uwsgi_body.t b/uwsgi_body.t --- a/uwsgi_body.t +++ b/uwsgi_body.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -23,7 +24,7 @@ select STDERR; $| = 1; select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http rewrite uwsgi/) - ->has_daemon('uwsgi')->plan(5) + ->has_daemon('uwsgi')->plan(7) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -46,6 +47,17 @@ http { uwsgi_pass 127.0.0.1:8081; uwsgi_param CONTENT_LENGTH $content_length if_not_empty; } + + location /nb/ { + uwsgi_pass 127.0.0.1:8081; + uwsgi_param CONTENT_LENGTH $content_length if_not_empty; + uwsgi_request_buffering off; + } + + location /discard/ { + client_max_body_size 1; + error_page 413 /error413; + } } } @@ -95,6 +107,12 @@ like(http_get_length('/', ''), qr/cl=0 ' like(http_get_chunked('/', 'foobar'), qr/cl=6 'foobar'/, 'uwsgi chunked'); like(http_get_chunked('/', ''), qr/cl=0 ''/, 'uwsgi empty chunked'); +like(http_get_nobuf('/', 'foo', 'bar'), qr/cl=6 'foobar'/, + 'uwsgi no request buffering'); + +like(http_get_nobuf('/discard/', 'foo', 'bar'), qr/SEE-THIS/, + 'uwsgi discard body'); + ############################################################################### sub http_get_length { @@ -126,4 +144,16 @@ 0 EOF } +sub http_get_nobuf { + my ($url, $body1, $body2) = @_; + my $length = length($body1) + length($body2); + return http(< 0.1, body => $body2); +GET $url HTTP/1.1 +Host: localhost +Connection: close +Content-Length: $length + +EOF +} + ############################################################################### From mdounin at mdounin.ru Mon May 18 16:41:45 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Mon, 18 May 2026 19:41:45 +0300 Subject: [PATCH 02 of 10] Rewrite: fixed incorrect initial escaping in "set" In-Reply-To: <071c2e847b986bb7b133.1778976755@vm-bsd.mdounin.ru> References: <9aadc356492de0e61c8e.1778976754@vm-bsd.mdounin.ru> <071c2e847b986bb7b133.1778976755@vm-bsd.mdounin.ru> Message-ID: Hello! On Sun, May 17, 2026 at 03:12:35AM +0300, Maxim Dounin wrote: > # HG changeset patch > # User Maxim Dounin > # Date 1778973194 -10800 > # Sun May 17 02:13:14 2026 +0300 > # Node ID 071c2e847b986bb7b133ff42c26f9aa2f55b102b > # Parent 9aadc356492de0e61c8ee202c246c2353e8fbc83 > Rewrite: fixed incorrect initial escaping in "set". > > Previously, the following configuration resulted in inconsistent escaping > of the $temp variable in requests to "/a/..." and "/b/...": > > map $uri $foo { > ~(.*) 1; > } > > location /a/ { > set $temp $foo:$1; > return 200 $temp; > } > > location /b/ { > rewrite ^(.*) $1; > set $temp $foo:$1; > return 200 $temp; > } > > In requests to "/a/..." the $1 capture contents were escaped when > copying to the $temp variable due to e->quote being initially set in > ngx_http_rewrite_handler(). And in requests to "/b/..." escaping was > not used, since e->quote was cleared by ngx_http_script_regex_start_code() > and ngx_http_script_regex_end_code(). Note well that in both cases $1 > comes from a regular expression in the map, and different behaviour here > is clearly unexpected. > > The "e->quote = 1;" initialization in ngx_http_rewrite_handler() was > originally introduced in 485:4ebe09b07e30 (0.1.17) along with the new > rewrite module, and had no effect at the time: e->quote was always set > before being used. It started to affect the module behaviour after the > introduction of the "set" directive in 501:d4ea69372b94 (0.1.25) and > variables support in "set" in 515:417a087c9c4d (0.1.32). > > With this change, e->quote is no longer set in ngx_http_rewrite_handler(), > resulting in consistent behaviour in all cases (and also matching the > behaviour seen without an intermediate "set"). > > diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c > --- a/src/http/modules/ngx_http_rewrite_module.c > +++ b/src/http/modules/ngx_http_rewrite_module.c > @@ -171,7 +171,6 @@ ngx_http_rewrite_handler(ngx_http_reques > > e->ip = rlcf->codes->elts; > e->request = r; > - e->quote = 1; > e->log = rlcf->log; > e->status = NGX_DECLINED; > Looking more into this, I tend to think that this change might introduce security risks to configurations which rely on escaping being applied by "set". A safer approach would be to make sure that escaping stays the same after a rewrite. Patch below. # HG changeset patch # User Maxim Dounin # Date 1779120864 -10800 # Mon May 18 19:14:24 2026 +0300 # Node ID f7578d17fb54a54b85000a5aebef6c77748c2f14 # Parent fb3ea1817a085ad147cf2f2fc6a87c01de243a69 Rewrite: fixed inconsistent escaping in "set". Previously, the following configuration resulted in inconsistent escaping of the $temp variable in requests to "/a/..." and "/b/...": location /a/ { if ($uri ~ (.*)) { set $temp $1; } return 200 $temp; } location /b/ { rewrite ^(.*) $1; if ($uri ~ (.*)) { set $temp $1; } return 200 $temp; } In requests to "/a/..." the $1 capture contents were escaped when copying to the $temp variable due to e->quote being initially set in ngx_http_rewrite_handler(). And in requests to "/b/..." escaping was not applied, since e->quote was cleared by ngx_http_script_regex_end_code(). With this change, the e->quote value set by ngx_http_script_regex_end_code() matches the initial value set by ngx_http_rewrite_handler(). As a result, escaping is now identical regardless of whether "set" is executed after a rewrite or not. Note that a better approach might be to avoid automatic escaping of positional captures by "set" altogether. However, this might put existing configurations at risk if they rely on escaping being applied. At the same time, if escaping is not desired, named captures can be used instead. diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1203,7 +1203,7 @@ ngx_http_script_regex_end_code(ngx_http_ r = e->request; - e->quote = 0; + e->quote = 1; e->is_args = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue May 19 02:46:30 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:30 +0300 Subject: [nginx] Rewrite: fixed incorrect escaping and possible segfault. Message-ID: details: http://freenginx.org/hg/nginx/rev/dc7cfc0037b8 branches: changeset: 9517:dc7cfc0037b8 user: Maxim Dounin date: Tue May 19 01:56:22 2026 +0300 description: Rewrite: fixed incorrect escaping and possible segfault. Similarly to 4617:972642646f06, the following code resulted in incorrect escaping of the $temp variable and possible segfault: location / { rewrite ^(.*) /uri?args; set $temp $1; return 200 "$temp"; } If there were arguments in rewrite's replacement string, the is_args flag was set and never cleared. This resulted in escaping being incorrectly applied to positional captures evaluated after the rewrite in the same script engine, notably in "set", "if", and "rewrite" directives. Additionally, in "set", "if", and "rewrite" with duplicate captures or additional variables, the buffer was allocated without escaping expected, so this also resulted in a buffer overrun and a possible segfault (CVE-2026-42945). The fix is to clear the is_args flag after rewrite evaluation in ngx_http_script_regex_end_code(), similarly to how we clear e->quote and e->args. Additionally, to ensure that buffer allocation stays correct even if the is_args flag is somehow set, e->is_args is now propagated to length calculations in ngx_http_script_regex_start_code() and in ngx_http_script_complex_value_code(). See also: https://github.com/nginx/nginx/commit/2046b45aa0c6e712c216b9075886f3f26e9b4ca9 diffstat: src/http/ngx_http_script.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diffs (27 lines): diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1161,6 +1161,7 @@ ngx_http_script_regex_start_code(ngx_htt le.line = e->line; le.request = r; le.quote = code->redirect; + le.is_args = e->is_args; len = 0; @@ -1203,6 +1204,7 @@ ngx_http_script_regex_end_code(ngx_http_ r = e->request; e->quote = 0; + e->is_args = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http script regex end"); @@ -1769,6 +1771,7 @@ ngx_http_script_complex_value_code(ngx_h le.line = e->line; le.request = e->request; le.quote = e->quote; + le.is_args = e->is_args; for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; From mdounin at mdounin.ru Tue May 19 02:46:30 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:30 +0300 Subject: [nginx] Rewrite: fixed inconsistent escaping in "set". Message-ID: details: http://freenginx.org/hg/nginx/rev/ee0b65c71b94 branches: changeset: 9518:ee0b65c71b94 user: Maxim Dounin date: Tue May 19 01:56:24 2026 +0300 description: Rewrite: fixed inconsistent escaping in "set". Previously, the following configuration resulted in inconsistent escaping of the $temp variable in requests to "/a/..." and "/b/...": location /a/ { if ($uri ~ (.*)) { set $temp $1; } return 200 $temp; } location /b/ { rewrite ^(.*) $1; if ($uri ~ (.*)) { set $temp $1; } return 200 $temp; } In requests to "/a/..." the $1 capture contents were escaped when copying to the $temp variable due to e->quote being initially set in ngx_http_rewrite_handler(). And in requests to "/b/..." escaping was not applied, since e->quote was cleared by ngx_http_script_regex_end_code(). With this change, the e->quote value set by ngx_http_script_regex_end_code() matches the initial value set by ngx_http_rewrite_handler(). As a result, escaping is now identical regardless of whether "set" is executed after a rewrite or not. Note that a better approach might be to avoid automatic escaping of positional captures by "set" altogether. However, this might put existing configurations at risk if they rely on escaping being applied. At the same time, if escaping is not desired, named captures can be used instead. diffstat: src/http/ngx_http_script.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (12 lines): diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1203,7 +1203,7 @@ ngx_http_script_regex_end_code(ngx_http_ r = e->request; - e->quote = 0; + e->quote = 1; e->is_args = 0; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, From mdounin at mdounin.ru Tue May 19 02:46:30 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:30 +0300 Subject: [nginx] Charset: fixed handling of incomplete UTF-8 characters. Message-ID: details: http://freenginx.org/hg/nginx/rev/5ebb2a99ad8d branches: changeset: 9519:5ebb2a99ad8d user: Maxim Dounin date: Tue May 19 01:56:27 2026 +0300 description: Charset: fixed handling of incomplete UTF-8 characters. Previously, if a UTF-8 character was split across multiple buffers, the second and subsequent buffers were handled incorrectly: ngx_decode_utf8() was called with the wrong size if there are fewer bytes in the buffer than ctx->saved can hold, the following code called ngx_memcpy() with the wrong size, potentially reading past the supplied buffer, and ctx->saved_len was set to an incorrect value, which could later result in reading before the buffer (CVE-2026-42934). The fix is to adjust the code to make sure that the "i" value properly represents the number of bytes available in ctx->saved in all cases, remove the unneeded ngx_memcpy() call, and set ctx->saved_len to the correct value. See also: https://github.com/nginx/nginx/commit/696a7f1b9198d576e6a59c1655b746fbf06561cf diffstat: src/http/modules/ngx_http_charset_filter_module.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-) diffs (24 lines): diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -788,8 +788,8 @@ ngx_http_charset_recode_from_utf8(ngx_po p = src; - for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) { - ctx->saved[i] = *p++; + for (i = ctx->saved_len; i < NGX_UTF_LEN; /* void */) { + ctx->saved[i++] = *p++; if (p == buf->last) { break; @@ -826,8 +826,7 @@ ngx_http_charset_recode_from_utf8(ngx_po b->sync = 1; b->shadow = buf; - ngx_memcpy(&ctx->saved[ctx->saved_len], src, i); - ctx->saved_len += i; + ctx->saved_len = i; return out; } From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] Charset: fixed reuse of buffers without associated memory. Message-ID: details: http://freenginx.org/hg/nginx/rev/317eafac071e branches: changeset: 9520:317eafac071e user: Maxim Dounin date: Tue May 19 01:56:29 2026 +0300 description: Charset: fixed reuse of buffers without associated memory. Previously, in ngx_http_charset_body_filter() buffers with b->pos set were assumed to have an associated memory allocation and were saved to the ctx->free_buffers list. However, b->pos is set in all buffers, including ones without associated memory. This was safe, since such buffers were ignored by ngx_http_charset_get_buffer(), yet reuse of such buffers via ngx_http_charset_get_buf() was broken. The fix is to check b->start instead in ngx_http_charset_body_filter(). To make sure cached buffers can actually be used, various internal fields are now properly cleared when using cached buffers. Also, b->sync is now properly cleared in cached buffers returned by ngx_http_charset_get_buffer(). And b->pos of the known-to-be-empty sync buffer allocated in ngx_http_charset_recode_from_utf8() is set to b->last, to avoid unexpected sizes shown in logs. diffstat: src/http/modules/ngx_http_charset_filter_module.c | 17 +++++++++++++---- 1 files changed, 13 insertions(+), 4 deletions(-) diffs (55 lines): diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -630,7 +630,7 @@ ngx_http_charset_body_filter(ngx_http_re b->shadow->pos = b->shadow->last; } - if (b->pos) { + if (b->start) { cl->next = ctx->free_buffers; ctx->free_buffers = cl; continue; @@ -821,7 +821,7 @@ ngx_http_charset_recode_from_utf8(ngx_po b = out->buf; - b->pos = buf->pos; + b->pos = buf->last; b->last = buf->last; b->sync = 1; b->shadow = buf; @@ -1094,15 +1094,23 @@ recode: static ngx_chain_t * ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx) { + ngx_buf_t *b; ngx_chain_t *cl; cl = ctx->free_bufs; if (cl) { ctx->free_bufs = cl->next; + cl->next = NULL; - cl->buf->shadow = NULL; - cl->next = NULL; + b = cl->buf; + + b->temporary = 0; + b->memory = 0; + b->mmap = 0; + b->flush = 0; + b->sync = 0; + b->shadow = NULL; return cl; } @@ -1144,6 +1152,7 @@ ngx_http_charset_get_buffer(ngx_pool_t * b->pos = b->start; b->temporary = 1; + b->sync = 0; b->shadow = NULL; return cl; From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] Charset: improved charset_map parsing. Message-ID: details: http://freenginx.org/hg/nginx/rev/88857e9af1fe branches: changeset: 9521:88857e9af1fe user: Maxim Dounin date: Tue May 19 01:56:31 2026 +0300 description: Charset: improved charset_map parsing. Previously, too long UTF-8 codes in charset_map resulted in writing outside of the allocated src2dst buffer, potentially causing a segmentation fault when parsing an invalid configuration. Also, the "dst > 255" check is unnecessary when parsing exactly two hex characters, and therefore was removed. See also: https://github.com/nginx/nginx/commit/a813c639211728a1441945dee149b44a0935f48b diffstat: src/http/modules/ngx_http_charset_filter_module.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diffs (22 lines): diff --git a/src/http/modules/ngx_http_charset_filter_module.c b/src/http/modules/ngx_http_charset_filter_module.c --- a/src/http/modules/ngx_http_charset_filter_module.c +++ b/src/http/modules/ngx_http_charset_filter_module.c @@ -1345,11 +1345,17 @@ ngx_http_charset_map(ngx_conf_t *cf, ngx if (ctx->charset->utf8) { p = &table->src2dst[src * NGX_UTF_LEN]; + if (value[1].len / 2 > NGX_UTF_LEN - 1) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "invalid value \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + *p++ = (u_char) (value[1].len / 2); for (i = 0; i < value[1].len; i += 2) { dst = ngx_hextoi(&value[1].data[i], 2); - if (dst == NGX_ERROR || dst > 255) { + if (dst == NGX_ERROR) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[1]); return NGX_CONF_ERROR; From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] Updated copyright year for upcoming patch imports. Message-ID: details: http://freenginx.org/hg/nginx/rev/f2e486b715cb branches: changeset: 9522:f2e486b715cb user: Maxim Dounin date: Tue May 19 01:56:34 2026 +0300 description: Updated copyright year for upcoming patch imports. diffstat: docs/text/LICENSE | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diffs (11 lines): diff --git a/docs/text/LICENSE b/docs/text/LICENSE --- a/docs/text/LICENSE +++ b/docs/text/LICENSE @@ -1,6 +1,6 @@ /* * Copyright (C) 2002-2021 Igor Sysoev - * Copyright (C) 2011-2024 Nginx, Inc. + * Copyright (C) 2011-2026 Nginx, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] OCSP: resolve cleanup on connection close. Message-ID: details: http://freenginx.org/hg/nginx/rev/2c3807852ed8 branches: changeset: 9523:2c3807852ed8 user: Roman Arutyunyan date: Tue Apr 21 14:51:41 2026 +0400 description: OCSP: resolve cleanup on connection close. Previously, when a client SSL connection was terminated (typically due to a timeout) while resolving an OCSP responder, the OCSP context was freed, but the resolve context was not. This resulted in use-after-free on resolve completion. Reported by Leo Lin. Obtained from: https://github.com/nginx/nginx/commit/71841dcedfdf46048ef5e25413fdf97a66957913 diffstat: src/event/ngx_event_openssl_stapling.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-) diffs (50 lines): diff --git a/src/event/ngx_event_openssl_stapling.c b/src/event/ngx_event_openssl_stapling.c --- a/src/event/ngx_event_openssl_stapling.c +++ b/src/event/ngx_event_openssl_stapling.c @@ -111,6 +111,7 @@ struct ngx_ssl_ocsp_ctx_s { ngx_resolver_t *resolver; ngx_msec_t resolver_timeout; + ngx_resolver_ctx_t *resolve; ngx_msec_t timeout; @@ -1303,6 +1304,10 @@ ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ct ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, "ssl ocsp done"); + if (ctx->resolve) { + ngx_resolve_name_done(ctx->resolve); + } + if (ctx->peer.connection) { ngx_close_connection(ctx->peer.connection); } @@ -1395,7 +1400,10 @@ ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t resolve->data = ctx; resolve->timeout = ctx->resolver_timeout; + ctx->resolve = resolve; + if (ngx_resolve_name(resolve) != NGX_OK) { + ctx->resolve = NULL; ngx_ssl_ocsp_error(ctx); return; } @@ -1484,6 +1492,7 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolve } ngx_resolve_name_done(resolve); + ctx->resolve = NULL; ngx_ssl_ocsp_connect(ctx); return; @@ -1491,6 +1500,8 @@ ngx_ssl_ocsp_resolve_handler(ngx_resolve failed: ngx_resolve_name_done(resolve); + ctx->resolve = NULL; + ngx_ssl_ocsp_error(ctx); } From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] QUIC: avoid assigning unvalidated address to new streams. Message-ID: details: http://freenginx.org/hg/nginx/rev/0ffc9191ae38 branches: changeset: 9524:0ffc9191ae38 user: Roman Arutyunyan date: Thu Apr 30 17:15:53 2026 +0400 description: QUIC: avoid assigning unvalidated address to new streams. Previously, when a client migrated to a new address, new QUIC streams received this address before validation. This allowed an attacker to create QUIC streams with a spoofed address. Reported by Rodrigo Laneth. Obtained from: https://github.com/nginx/nginx/commit/f37ec3e5d4f527e52ed5b25951ad8aa7d1ff6266 diffstat: src/event/quic/ngx_event_quic_migration.c | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diffs (34 lines): diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_event_quic_migration.c --- a/src/event/quic/ngx_event_quic_migration.c +++ b/src/event/quic/ngx_event_quic_migration.c @@ -193,6 +193,8 @@ valid: path->validated = 1; + ngx_quic_set_connection_path(c, path); + if (path->mtu_unvalidated) { path->mtu_unvalidated = 0; return ngx_quic_validate_path(c, path); @@ -510,9 +512,10 @@ ngx_quic_handle_migration(ngx_connection qc->path = next; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, next); + if (next->validated) { + ngx_quic_set_connection_path(c, next); - if (!next->validated && next->state != NGX_QUIC_PATH_VALIDATING) { + } else if (next->state != NGX_QUIC_PATH_VALIDATING) { if (ngx_quic_validate_path(c, next) != NGX_OK) { return NGX_ERROR; } @@ -806,8 +809,6 @@ ngx_quic_expire_path_validation(ngx_conn qc->path = bkp; qc->path->tag = NGX_QUIC_PATH_ACTIVE; - ngx_quic_set_connection_path(c, qc->path); - ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic path seq:%uL addr:%V is restored from backup", qc->path->seqnum, &qc->path->addr_text); From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] Upstream: reset parsing state after invalid status line. Message-ID: details: http://freenginx.org/hg/nginx/rev/a2337bcaaea6 branches: changeset: 9525:a2337bcaaea6 user: Sergey Kandaurov date: Wed Apr 29 21:56:51 2026 +0400 description: Upstream: reset parsing state after invalid status line. Previously, it was possible to start parsing headers with a wrong parsing state after status line was not recognized, as a fallback used in the scgi and uwsgi modules. Reported by Leo Lin. Obtained from: https://github.com/nginx/nginx/commit/f79c286b34d3b708bd4856a56e27529e11386098 diffstat: src/http/modules/ngx_http_scgi_module.c | 1 + src/http/modules/ngx_http_uwsgi_module.c | 1 + 2 files changed, 2 insertions(+), 0 deletions(-) diffs (22 lines): 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 @@ -1014,6 +1014,7 @@ ngx_http_scgi_process_status_line(ngx_ht if (rc == NGX_ERROR) { u->process_header = ngx_http_scgi_process_header; + r->state = 0; return ngx_http_scgi_process_header(r); } 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 @@ -1245,6 +1245,7 @@ ngx_http_uwsgi_process_status_line(ngx_h if (rc == NGX_ERROR) { u->process_header = ngx_http_uwsgi_process_header; + r->state = 0; return ngx_http_uwsgi_process_header(r); } From mdounin at mdounin.ru Tue May 19 02:46:31 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 05:46:31 +0300 Subject: [nginx] Upstream: fixed parsing of split status lines. Message-ID: details: http://freenginx.org/hg/nginx/rev/2b3e6ce20c69 branches: changeset: 9526:2b3e6ce20c69 user: Sergey Kandaurov date: Wed Apr 29 23:02:20 2026 +0400 description: Upstream: fixed parsing of split status lines. If the first response line was split across reads and it didn't appear a status line, the portion already processed was lost. The change introduces a new field for proper backtracking on status line fallback. Obtained from (with minor changes): https://github.com/nginx/nginx/commit/5f86648ef8c969e98aa2f7b938472296b12055be diffstat: src/http/modules/ngx_http_proxy_module.c | 2 ++ src/http/modules/ngx_http_scgi_module.c | 1 + src/http/modules/ngx_http_uwsgi_module.c | 1 + src/http/ngx_http.h | 1 + src/http/ngx_http_parse.c | 2 ++ 5 files changed, 7 insertions(+), 0 deletions(-) diffs (57 lines): 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 @@ -1862,6 +1862,8 @@ ngx_http_proxy_process_status_line(ngx_h u->headers_in.status_n = 200; u->headers_in.connection_close = 1; + u->buffer.pos = ctx->status.line_start; + return NGX_OK; } 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 @@ -1014,6 +1014,7 @@ ngx_http_scgi_process_status_line(ngx_ht if (rc == NGX_ERROR) { u->process_header = ngx_http_scgi_process_header; + u->buffer.pos = status->line_start; r->state = 0; return ngx_http_scgi_process_header(r); } 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 @@ -1245,6 +1245,7 @@ ngx_http_uwsgi_process_status_line(ngx_h if (rc == NGX_ERROR) { u->process_header = ngx_http_uwsgi_process_header; + u->buffer.pos = status->line_start; r->state = 0; return ngx_http_uwsgi_process_header(r); } diff --git a/src/http/ngx_http.h b/src/http/ngx_http.h --- a/src/http/ngx_http.h +++ b/src/http/ngx_http.h @@ -72,6 +72,7 @@ struct ngx_http_chunked_s { typedef struct { ngx_uint_t http_version; ngx_uint_t code; + u_char *line_start; u_char *start; u_char *end; } ngx_http_status_t; diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c --- a/src/http/ngx_http_parse.c +++ b/src/http/ngx_http_parse.c @@ -1663,6 +1663,8 @@ ngx_http_parse_status_line(ngx_http_requ /* "HTTP/" */ case sw_start: + status->line_start = p; + switch (ch) { case 'H': state = sw_H; From mdounin at mdounin.ru Tue May 19 13:49:09 2026 From: mdounin at mdounin.ru (Maxim Dounin) Date: Tue, 19 May 2026 16:49:09 +0300 Subject: freenginx-1.31.1 changes draft Message-ID: Hello! Changes with freenginx 1.31.1 19 May 2026 *) Feature: the "off" parameter of the "index" directive. Thanks to Fabiano Furtado. *) Bugfix: a segmentation fault might occur in a worker process if the "rewrite" directive was used to change request arguments and other directives of the ngx_http_rewrite_module were executed afterwards. *) Bugfix: in the "set" directive. *) Bugfix: a segmentation fault might occur in a worker process if the ngx_http_charset_module was used to convert responses from UTF-8. *) Bugfix: in the ngx_http_charset_module. *) Bugfix: a segmentation fault might occur in a worker process if the "ssl_ocsp" directive was used. *) Bugfix: a segmentation fault might occur in a worker process if the "scgi_pass" or "uwsgi_pass" directives were used. *) Bugfix: in the "scgi_request_buffering" directive. *) Bugfix: in HTTP/3. ????????? ? freenginx 1.31.1 19.05.2026 *) ??????????: ???????? off ? ????????? index. ??????? Fabiano Furtado. *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, ???? ????????? rewrite ?????????????? ??? ????????? ?????????? ??????? ? ????? ??? ??????????? ?????? ????????? ?????? ngx_http_rewrite_module. *) ???????????: ? ????????? set. *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, ???? ?????? ngx_http_charset_module ????????????? ??? ??????????????? ??????? ?? UTF-8. *) ???????????: ? ?????? ngx_http_charset_module. *) ???????????: ? ?????? ???????? ??? ????????? segmentation fault, ???? ?????????????? ????????? ssl_ocsp. *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, ???? ?????????????? ????????? scgi_pass ??? uwsgi_pass. *) ???????????: ? ????????? scgi_request_buffering. *) ???????????: ? HTTP/3. -- Maxim Dounin http://mdounin.ru/ From mdounin at mdounin.ru Tue May 19 15:10:14 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 18:10:14 +0300 Subject: [nginx-site] Documented "index off;". Message-ID: details: http://freenginx.org/hg/nginx-site/rev/f104c00a4052 branches: changeset: 3133:f104c00a4052 user: Maxim Dounin date: Wed May 13 02:01:37 2026 +0300 description: Documented "index off;". diffstat: xml/en/docs/http/ngx_http_index_module.xml | 3 ++- xml/ru/docs/http/ngx_http_index_module.xml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diffs (41 lines): diff --git a/xml/en/docs/http/ngx_http_index_module.xml b/xml/en/docs/http/ngx_http_index_module.xml --- a/xml/en/docs/http/ngx_http_index_module.xml +++ b/xml/en/docs/http/ngx_http_index_module.xml @@ -43,7 +43,7 @@ location / {
-file ... +file ... | off index.html http server @@ -54,6 +54,7 @@ Defines files that will be used as an in The file name can contain variables. Files are checked in the specified order. The last element of the list can be a file with an absolute path. +The off parameter (1.31.1) disables use of index files. Example: index index.$geo.html index.0.html /index.html; diff --git a/xml/ru/docs/http/ngx_http_index_module.xml b/xml/ru/docs/http/ngx_http_index_module.xml --- a/xml/ru/docs/http/ngx_http_index_module.xml +++ b/xml/ru/docs/http/ngx_http_index_module.xml @@ -42,7 +42,7 @@ location / {
-???? ... +???? ... | off index.html http server @@ -53,6 +53,8 @@ location / { ? ????? ????? ????? ???????????? ??????????. ??????? ?????? ??????????? ? ??????? ?? ????????????. ? ????? ?????? ????? ?????? ???? ? ?????????? ?????. +???????? off (1.31.1) ????????? ????????????? +????????? ??????. ??????: index index.$geo.html index.0.html /index.html; From mdounin at mdounin.ru Tue May 19 15:11:21 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 18:11:21 +0300 Subject: [nginx] freenginx-1.31.1-RELEASE Message-ID: details: http://freenginx.org/hg/nginx/rev/0417363a5493 branches: changeset: 9527:0417363a5493 user: Maxim Dounin date: Tue May 19 18:08:03 2026 +0300 description: freenginx-1.31.1-RELEASE diffstat: docs/xml/nginx/changes.xml | 100 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 100 insertions(+), 0 deletions(-) diffs (110 lines): diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml --- a/docs/xml/nginx/changes.xml +++ b/docs/xml/nginx/changes.xml @@ -7,6 +7,106 @@
+ + + + +???????? off ? ????????? index.
+??????? Fabiano Furtado. +
+ +the "off" parameter of the "index" directive.
+Thanks to Fabiano Furtado. +
+
+ + + +? ??????? ???????? ??? ????????? segmentation fault, +???? ????????? rewrite ?????????????? ??? ????????? ?????????? ??????? +? ????? ??? ??????????? ?????? ????????? ?????? ngx_http_rewrite_module. + + +a segmentation fault might occur in a worker process +if the "rewrite" directive was used to change request arguments +and other directives of the ngx_http_rewrite_module were executed afterwards. + + + + + +? ????????? set. + + +in the "set" directive. + + + + + +? ??????? ???????? ??? ????????? segmentation fault, +???? ?????? ngx_http_charset_module ????????????? +??? ??????????????? ??????? ?? UTF-8. + + +a segmentation fault might occur in a worker process +if the ngx_http_charset_module was used +to convert responses from UTF-8. + + + + + +? ?????? ngx_http_charset_module. + + +in the ngx_http_charset_module. + + + + + +? ??????? ???????? ??? ????????? segmentation fault, +???? ?????????????? ????????? ssl_ocsp. + + +a segmentation fault might occur in a worker process +if the "ssl_ocsp" directive was used. + + + + + +? ??????? ???????? ??? ????????? segmentation fault, +???? ?????????????? ????????? scgi_pass ??? uwsgi_pass. + + +a segmentation fault might occur in a worker process +if the "scgi_pass" or "uwsgi_pass" directives were used. + + + + + +? ????????? scgi_request_buffering. + + +in the "scgi_request_buffering" directive. + + + + + +? HTTP/3. + + +in HTTP/3. + + + +
+ + From mdounin at mdounin.ru Tue May 19 15:11:21 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 18:11:21 +0300 Subject: [nginx] release-1.31.1 tag Message-ID: details: http://freenginx.org/hg/nginx/rev/d374a55efdb6 branches: changeset: 9528:d374a55efdb6 user: Maxim Dounin date: Tue May 19 18:08:04 2026 +0300 description: release-1.31.1 tag diffstat: .hgtags | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diffs (8 lines): diff --git a/.hgtags b/.hgtags --- a/.hgtags +++ b/.hgtags @@ -494,3 +494,4 @@ 4f4280557d20bc46ebbdc240ffd365f5ca6ce939 e4207f631186855d37ac286799c8cd4c9477d166 release-1.29.6 cac0fa5721386abbec57dcc2bb317f2531456e19 release-1.29.7 b1585cfeee5759a1ae3794b5c9c4c541c3c73a35 release-1.31.0 +0417363a549372cf4b20c611c6f14944b177e86c release-1.31.1 From mdounin at mdounin.ru Tue May 19 15:18:01 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 18:18:01 +0300 Subject: [nginx-site] freenginx-1.31.1 Message-ID: details: http://freenginx.org/hg/nginx-site/rev/c4e0e16ea404 branches: changeset: 3134:c4e0e16ea404 user: Maxim Dounin date: Tue May 19 18:14:49 2026 +0300 description: freenginx-1.31.1 diffstat: text/en/CHANGES | 27 +++++++++++++++++++++++++++ text/ru/CHANGES.ru | 29 +++++++++++++++++++++++++++++ xml/index.xml | 7 +++++++ xml/versions.xml | 1 + 4 files changed, 64 insertions(+), 0 deletions(-) diffs (100 lines): diff --git a/text/en/CHANGES b/text/en/CHANGES --- a/text/en/CHANGES +++ b/text/en/CHANGES @@ -1,4 +1,31 @@ +Changes with freenginx 1.31.1 19 May 2026 + + *) Feature: the "off" parameter of the "index" directive. + Thanks to Fabiano Furtado. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "rewrite" directive was used to change request arguments and other + directives of the ngx_http_rewrite_module were executed afterwards. + + *) Bugfix: in the "set" directive. + + *) Bugfix: a segmentation fault might occur in a worker process if the + ngx_http_charset_module was used to convert responses from UTF-8. + + *) Bugfix: in the ngx_http_charset_module. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "ssl_ocsp" directive was used. + + *) Bugfix: a segmentation fault might occur in a worker process if the + "scgi_pass" or "uwsgi_pass" directives were used. + + *) Bugfix: in the "scgi_request_buffering" directive. + + *) Bugfix: in HTTP/3. + + Changes with freenginx 1.31.0 05 May 2026 *) Change: now the "try_files" directive can be used with arguments diff --git a/text/ru/CHANGES.ru b/text/ru/CHANGES.ru --- a/text/ru/CHANGES.ru +++ b/text/ru/CHANGES.ru @@ -1,4 +1,33 @@ +????????? ? freenginx 1.31.1 19.05.2026 + + *) ??????????: ???????? off ? ????????? index. + ??????? Fabiano Furtado. + + *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, + ???? ????????? rewrite ?????????????? ??? ????????? ?????????? + ??????? ? ????? ??? ??????????? ?????? ????????? ?????? + ngx_http_rewrite_module. + + *) ???????????: ? ????????? set. + + *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, + ???? ?????? ngx_http_charset_module ????????????? ??? ??????????????? + ??????? ?? UTF-8. + + *) ???????????: ? ?????? ngx_http_charset_module. + + *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, + ???? ?????????????? ????????? ssl_ocsp. + + *) ???????????: ? ??????? ???????? ??? ????????? segmentation fault, + ???? ?????????????? ????????? scgi_pass ??? uwsgi_pass. + + *) ???????????: ? ????????? scgi_request_buffering. + + *) ???????????: ? HTTP/3. + + ????????? ? freenginx 1.31.0 05.05.2026 *) ?????????: ?????? ????????? try_files ????? ???????????? ? diff --git a/xml/index.xml b/xml/index.xml --- a/xml/index.xml +++ b/xml/index.xml @@ -8,6 +8,13 @@ + + +freenginx-1.31.1 +mainline version has been released. + + + freenginx-1.31.0 diff --git a/xml/versions.xml b/xml/versions.xml --- a/xml/versions.xml +++ b/xml/versions.xml @@ -9,6 +9,7 @@ + From mdounin at mdounin.ru Tue May 19 19:12:23 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 22:12:23 +0300 Subject: [PATCH] Tests: charset tests with multiple buffers Message-ID: <2d2b3edb6935fa91060b.1779217943@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1779217917 -10800 # Tue May 19 22:11:57 2026 +0300 # Node ID 2d2b3edb6935fa91060b3e2e804eca7927b93e81 # Parent dca9f4b8950e9e8473eead25c82d2314365f8481 Tests: charset tests with multiple buffers. diff --git a/charset_perl.t b/charset_perl.t new file mode 100644 --- /dev/null +++ b/charset_perl.t @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for charset filter, extended tests using embedded perl. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http charset perl/)->plan(1) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + charset test; + source_charset utf-8; + + charset_map test utf-8 { + 43 C2A9 ; # (C) + 54 E284A2 ; # trade mark sign + } + + postpone_output 0; + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location / { + perl 'sub { + my $r = shift; + $r->send_http_header("text/html"); + return OK if $r->header_only; + + # 2-byte character + + $r->print("\xc2\xa9"); + $r->print("\xc2"); + $r->print("\xa9"); + + # 3-byte character + + $r->print("\xe2\x84\xa2"); + $r->print("\xe2"); + $r->print("\x84"); + $r->print("\xa2"); + + # 4-byte character + + $r->print("\xf0\x90\x80\x80"); + $r->print("\xf0"); + $r->print("\x90"); + $r->print("\x80"); + $r->print("\x80"); + + return OK; + }'; + } + } +} + +EOF + +$t->run(); + +############################################################################### + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/multi'), qr/^CCTT𐀀𐀀$/m, 'multiple buffers'); + +} + +############################################################################### From mdounin at mdounin.ru Tue May 19 19:13:46 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Tue, 19 May 2026 22:13:46 +0300 Subject: [PATCH] Tests: additional rewrite and set tests Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779217965 -10800 # Tue May 19 22:12:45 2026 +0300 # Node ID ed704b735aa4530b758d3af2741046b0a888d07e # Parent 2d2b3edb6935fa91060b3e2e804eca7927b93e81 Tests: additional rewrite and set tests. diff --git a/rewrite.t b/rewrite.t --- a/rewrite.t +++ b/rewrite.t @@ -21,7 +21,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(23) +my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(24) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -126,7 +126,13 @@ http { return 200 "uri:$uri args:$args"; } - location /capturedup { + location /capture_dup { + rewrite ^(.*) $1?c=$1; + return 200 "uri:$uri args:$args"; + } + + location /capture_another { + rewrite ^(.*) $1?c=d; rewrite ^(.*) $1?c=$1; return 200 "uri:$uri args:$args"; } @@ -233,9 +239,22 @@ like(http_get('/capture/%25?a=b'), qr!^uri:/capture/% args:c=d&a=b$!ms, 'escape with added args'); -like(http_get('/capturedup/%25?a=b'), - qr!^uri:/capturedup/% args:c=/capturedup/%25&a=b$!ms, - 'escape with added args'); +like(http_get('/capture_dup/%25?a=b'), + qr!^uri:/capture_dup/% args:c=/capture_dup/%25&a=b$!ms, + 'escape with added args and duplicate captures'); + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/capture_another/%25?a=b'), + qr!^uri:/capture_another/% args:c=/capture_another/%25&c=d&a=b$!ms, + 'escape with added args and another rewrite'); + +} # break diff --git a/rewrite_set.t b/rewrite_set.t --- a/rewrite_set.t +++ b/rewrite_set.t @@ -22,7 +22,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(4); +my $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(10); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -40,18 +40,57 @@ http { listen 127.0.0.1:8080; server_name localhost; - ssi on; + location /string { + set $temp "set_string"; + return 200 "X${temp}X"; + } + + location /variable/ { + set $temp "set_$uri"; + return 200 "X${temp}X"; + } + + location ~ ^(/capture/.*) { + set $temp "set_$1"; + return 200 "X${temp}X"; + } + + location /if/ { + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } + + location /rewrite/ { + rewrite (.*) $1; + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } + + location /args/ { + rewrite (.*) $1?args; + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } location /t1 { set $http_foo "set_foo"; + ssi on; return 200 'XX'; } location /t2 { + ssi on; return 200 'XX'; } location /t3 { + ssi on; return 200 'XX'; } @@ -60,8 +99,8 @@ http { return 200 "X${http_connection}X\n"; } - # set in other context location /other { + # set in other context set $http_bar "set_bar"; } } @@ -73,15 +112,48 @@ EOF ############################################################################### -# prefixed variables +# basic set operations + +like(http_get('/string'), qr/Xset_stringX/, 'set string'); +like(http_get('/variable/%20x'), qr!Xset_/variable/ xX!, 'set variable'); +like(http_get('/capture/%20x'), qr!Xset_/capture/%20xX!, 'set capture'); +like(http_get('/if/%20x'), qr!Xset_/if/%20xX!, 'set capture after if'); + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); + +# set after a rewrite, used to loss quoting +# due to e->quote being reset + +like(http_get('/rewrite/%20x'), qr!Xset_/rewrite/%20xX!, + 'set capture after rewrite'); + +} + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +# set after a rewrite with arguments, +# used to incorrectly allocate buffer due to e->is_args set +# but not propagated to length calculations + +like(http_get('/args/%20x'), qr!Xset_/args/%20xX!, + 'set capture after rewrite with arguments'); + +} + +# non-indexed access of prefixed variables like(http_get_extra('/t1.html', 'Foo: http_foo'), qr/Xset_fooX/, 'set in this context'); like(http_get_extra('/t2.html', 'Bar: http_bar'), qr/Xhttp_barX/, 'set in other context'); - like(http_get_extra('/t3.html', 'Baz: http_baz'), qr/Xhttp_bazX/, 'not set'); - like(http_get('/t4.html'), qr/XbarX/, 'set get in return'); ############################################################################### From mdounin at mdounin.ru Wed May 20 00:35:35 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Wed, 20 May 2026 03:35:35 +0300 Subject: [PATCH] Tests: added tests for status lines split between packets Message-ID: <8b9c3815c15f78531163.1779237335@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1779237152 -10800 # Wed May 20 03:32:32 2026 +0300 # Node ID 8b9c3815c15f78531163e611e08f0369ef86b592 # Parent ed704b735aa4530b758d3af2741046b0a888d07e Tests: added tests for status lines split between packets. diff --git a/proxy_status.t b/proxy_status.t --- a/proxy_status.t +++ b/proxy_status.t @@ -57,7 +57,7 @@ http { EOF $t->run_daemon(\&http_daemon); -$t->try_run('no proxy_allow_http09')->plan(13); +$t->try_run('no proxy_allow_http09')->plan(14); $t->waitforsocket('127.0.0.1:' . port(8081)); ############################################################################### @@ -79,6 +79,14 @@ like(http_get('/http09'), qr!^HTTP/1.1 5 like(http_get('/allow09/http09'), qr!^HTTP/1.1 200 .*HTTP/0.9!s, 'http 0.9 allowed'); +TODO: { +local $TODO = 'not yet' unless $t->has_version('1.31.1'); + +like(http_get('/allow09/split'), qr!^HTTP/1.1 200 OK.*HTTP/0.9!s, + 'http 0.9 split between packets'); + +} + # spaces between digits not allowed since 1.29.1 like(http_get('/spaces'), qr!^HTTP/1.1 502 !s, 'status with spaces rejected'); @@ -157,6 +165,12 @@ sub http_daemon { print $client 'It is HTTP/0.9 response' . CRLF; + } elsif ($uri =~ m!/split!) { + + print $client 'HTTP'; + select undef, undef, undef, 0.1; + print $client '/0.9 split between packets'. CRLF; + } elsif ($uri =~ m!/spaces!) { print $client diff --git a/scgi_status.t b/scgi_status.t --- a/scgi_status.t +++ b/scgi_status.t @@ -25,7 +25,7 @@ eval { require SCGI; }; plan(skip_all => 'SCGI not installed') if $@; my $t = Test::Nginx->new() - ->has(qw/http scgi/)->plan(11) + ->has(qw/http scgi/)->plan(12) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -89,6 +89,18 @@ like(http_get('/001'), qr!^HTTP/1.1 502 } +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'leaves coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/split'), qr!^HTTP/1.1 200 .*HTTP-Header: foo!s, + 'status line split between packets'); + +} + ############################################################################### sub scgi_daemon { @@ -146,6 +158,12 @@ sub scgi_daemon { } elsif ($uri eq '/001') { $c->print("Status: 001 Invalid\n\n"); + + } elsif ($uri eq '/split') { + $c->print("HTTP"); + select undef, undef, undef, 0.1; + $c->print("-Header: foo\n"); + $c->print("Status: 200 OK\n\n"); } } } From mdounin at mdounin.ru Sat May 23 21:33:59 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Sun, 24 May 2026 00:33:59 +0300 Subject: [PATCH] Tests: improved ssl_verify_depth.t error reporting Message-ID: <2688ca1e99fb3b56e883.1779572039@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1779554679 -10800 # Sat May 23 19:44:39 2026 +0300 # Node ID 2688ca1e99fb3b56e8837c7d45c23e2f1c9eacdd # Parent 8b9c3815c15f78531163e611e08f0369ef86b592 Tests: improved ssl_verify_depth.t error reporting. Previously, the test was skipped with LibreSSL due to issues with verify depth handling introduced in LibreSSL 3.4.0, and depth (mis)handling in OpenSSL 1.1.0+ was silently accepted. With this change, tests that are expected to fail with the particular library version are marked as TODO with an explanation. diff --git a/ssl_verify_depth.t b/ssl_verify_depth.t --- a/ssl_verify_depth.t +++ b/ssl_verify_depth.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -25,8 +26,6 @@ select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/) ->has_daemon('openssl'); -plan(skip_all => 'LibreSSL') if $t->has_module('LibreSSL'); - $t->plan(9)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -148,10 +147,28 @@ system("openssl ca -batch -config $d/ca. # as a result, it is not possible to limit certificate checking # to self-signed certificates only when using OpenSSL 1.1.0+ +# LibreSSL 3.4.0+ interprets verify depth 0 as no limit + like(get(8080, 'root'), qr/SUCCESS/, 'verify depth 0 - root'); -like(get(8080, 'int'), qr/FAI|SUC/, 'verify depth 0 - no int'); + +TODO: { +local $TODO = 'off-by-one depth in OpenSSL 1.1.0+' + if $t->has_feature('openssl:1.1.0'); +local $TODO = 'no zero depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0'); + +like(get(8080, 'int'), qr/FAILED/, 'verify depth 0 - no int'); + +} + +TODO: { +local $TODO = 'no zero depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0'); + like(get(8080, 'end'), qr/FAILED/, 'verify depth 0 - no end'); +} + # with verify depth 1 (the default), one signature is # expected to be checked, so certificates directly signed # by the root cert are allowed, but nothing more @@ -160,9 +177,25 @@ like(get(8080, 'end'), qr/FAILED/, 've # so with depth 1 it is possible to validate not only directly signed # certificates, but also chains with one intermediate certificate +# LibreSSL 3.4.0+ ignores depth limit as long as verify callback returns 1; +# fixed in LibreSSL 4.3.0, broken again in master after LibreSSL 4.3.1 +# (as seen on OpenBSD 7.9, with LibreSSL version reported as 4.3.0) + like(get(8081, 'root'), qr/SUCCESS/, 'verify depth 1 - root'); like(get(8081, 'int'), qr/SUCCESS/, 'verify depth 1 - int'); -like(get(8081, 'end'), qr/FAI|SUC/, 'verify depth 1 - no end'); + +TODO: { +local $TODO = 'off-by-one depth in OpenSSL 1.1.0+' + if $t->has_feature('openssl:1.1.0'); +local $TODO = 'ignored depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0') + and not $t->has_feature('libressl:4.3.0') + or $t->has_feature('libressl:4.3.0') + and $^O eq 'openbsd'; + +like(get(8081, 'end'), qr/FAILED/, 'verify depth 1 - no end'); + +} # with verify depth 2 it is also possible to validate up to two signatures, # so chains with one intermediate certificate are allowed From mdounin at mdounin.ru Sun May 24 21:30:26 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 00:30:26 +0300 Subject: [nginx-tests] Tests: charset tests with multiple buffers. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/2d2b3edb6935 branches: changeset: 2057:2d2b3edb6935 user: Maxim Dounin date: Tue May 19 22:11:57 2026 +0300 description: Tests: charset tests with multiple buffers. diffstat: charset_perl.t | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 101 insertions(+), 0 deletions(-) diffs (106 lines): diff --git a/charset_perl.t b/charset_perl.t new file mode 100644 --- /dev/null +++ b/charset_perl.t @@ -0,0 +1,101 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Tests for charset filter, extended tests using embedded perl. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http charset perl/)->plan(1) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + charset test; + source_charset utf-8; + + charset_map test utf-8 { + 43 C2A9 ; # (C) + 54 E284A2 ; # trade mark sign + } + + postpone_output 0; + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location / { + perl 'sub { + my $r = shift; + $r->send_http_header("text/html"); + return OK if $r->header_only; + + # 2-byte character + + $r->print("\xc2\xa9"); + $r->print("\xc2"); + $r->print("\xa9"); + + # 3-byte character + + $r->print("\xe2\x84\xa2"); + $r->print("\xe2"); + $r->print("\x84"); + $r->print("\xa2"); + + # 4-byte character + + $r->print("\xf0\x90\x80\x80"); + $r->print("\xf0"); + $r->print("\x90"); + $r->print("\x80"); + $r->print("\x80"); + + return OK; + }'; + } + } +} + +EOF + +$t->run(); + +############################################################################### + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/multi'), qr/^CCTT𐀀𐀀$/m, 'multiple buffers'); + +} + +############################################################################### From mdounin at mdounin.ru Sun May 24 21:30:26 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 00:30:26 +0300 Subject: [nginx-tests] Tests: additional rewrite and set tests. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/ed704b735aa4 branches: changeset: 2058:ed704b735aa4 user: Maxim Dounin date: Tue May 19 22:12:45 2026 +0300 description: Tests: additional rewrite and set tests. diffstat: rewrite.t | 29 ++++++++++++++++--- rewrite_set.t | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 102 insertions(+), 11 deletions(-) diffs (186 lines): diff --git a/rewrite.t b/rewrite.t --- a/rewrite.t +++ b/rewrite.t @@ -21,7 +21,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(23) +my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(24) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -126,7 +126,13 @@ http { return 200 "uri:$uri args:$args"; } - location /capturedup { + location /capture_dup { + rewrite ^(.*) $1?c=$1; + return 200 "uri:$uri args:$args"; + } + + location /capture_another { + rewrite ^(.*) $1?c=d; rewrite ^(.*) $1?c=$1; return 200 "uri:$uri args:$args"; } @@ -233,9 +239,22 @@ like(http_get('/capture/%25?a=b'), qr!^uri:/capture/% args:c=d&a=b$!ms, 'escape with added args'); -like(http_get('/capturedup/%25?a=b'), - qr!^uri:/capturedup/% args:c=/capturedup/%25&a=b$!ms, - 'escape with added args'); +like(http_get('/capture_dup/%25?a=b'), + qr!^uri:/capture_dup/% args:c=/capture_dup/%25&a=b$!ms, + 'escape with added args and duplicate captures'); + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/capture_another/%25?a=b'), + qr!^uri:/capture_another/% args:c=/capture_another/%25&c=d&a=b$!ms, + 'escape with added args and another rewrite'); + +} # break diff --git a/rewrite_set.t b/rewrite_set.t --- a/rewrite_set.t +++ b/rewrite_set.t @@ -22,7 +22,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(4); +my $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(10); $t->write_file_expand('nginx.conf', <<'EOF'); @@ -40,18 +40,57 @@ http { listen 127.0.0.1:8080; server_name localhost; - ssi on; + location /string { + set $temp "set_string"; + return 200 "X${temp}X"; + } + + location /variable/ { + set $temp "set_$uri"; + return 200 "X${temp}X"; + } + + location ~ ^(/capture/.*) { + set $temp "set_$1"; + return 200 "X${temp}X"; + } + + location /if/ { + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } + + location /rewrite/ { + rewrite (.*) $1; + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } + + location /args/ { + rewrite (.*) $1?args; + if ($uri ~ "(.*)") { + set $temp "set_$1"; + } + return 200 "X${temp}X"; + } location /t1 { set $http_foo "set_foo"; + ssi on; return 200 'XX'; } location /t2 { + ssi on; return 200 'XX'; } location /t3 { + ssi on; return 200 'XX'; } @@ -60,8 +99,8 @@ http { return 200 "X${http_connection}X\n"; } - # set in other context location /other { + # set in other context set $http_bar "set_bar"; } } @@ -73,15 +112,48 @@ EOF ############################################################################### -# prefixed variables +# basic set operations + +like(http_get('/string'), qr/Xset_stringX/, 'set string'); +like(http_get('/variable/%20x'), qr!Xset_/variable/ xX!, 'set variable'); +like(http_get('/capture/%20x'), qr!Xset_/capture/%20xX!, 'set capture'); +like(http_get('/if/%20x'), qr!Xset_/if/%20xX!, 'set capture after if'); + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); + +# set after a rewrite, used to loss quoting +# due to e->quote being reset + +like(http_get('/rewrite/%20x'), qr!Xset_/rewrite/%20xX!, + 'set capture after rewrite'); + +} + +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +# set after a rewrite with arguments, +# used to incorrectly allocate buffer due to e->is_args set +# but not propagated to length calculations + +like(http_get('/args/%20x'), qr!Xset_/args/%20xX!, + 'set capture after rewrite with arguments'); + +} + +# non-indexed access of prefixed variables like(http_get_extra('/t1.html', 'Foo: http_foo'), qr/Xset_fooX/, 'set in this context'); like(http_get_extra('/t2.html', 'Bar: http_bar'), qr/Xhttp_barX/, 'set in other context'); - like(http_get_extra('/t3.html', 'Baz: http_baz'), qr/Xhttp_bazX/, 'not set'); - like(http_get('/t4.html'), qr/XbarX/, 'set get in return'); ############################################################################### From mdounin at mdounin.ru Sun May 24 21:30:26 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 00:30:26 +0300 Subject: [nginx-tests] Tests: added tests for status lines split between ... Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/8b9c3815c15f branches: changeset: 2059:8b9c3815c15f user: Maxim Dounin date: Wed May 20 03:32:32 2026 +0300 description: Tests: added tests for status lines split between packets. diffstat: proxy_status.t | 16 +++++++++++++++- scgi_status.t | 20 +++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diffs (84 lines): diff --git a/proxy_status.t b/proxy_status.t --- a/proxy_status.t +++ b/proxy_status.t @@ -57,7 +57,7 @@ http { EOF $t->run_daemon(\&http_daemon); -$t->try_run('no proxy_allow_http09')->plan(13); +$t->try_run('no proxy_allow_http09')->plan(14); $t->waitforsocket('127.0.0.1:' . port(8081)); ############################################################################### @@ -79,6 +79,14 @@ like(http_get('/http09'), qr!^HTTP/1.1 5 like(http_get('/allow09/http09'), qr!^HTTP/1.1 200 .*HTTP/0.9!s, 'http 0.9 allowed'); +TODO: { +local $TODO = 'not yet' unless $t->has_version('1.31.1'); + +like(http_get('/allow09/split'), qr!^HTTP/1.1 200 OK.*HTTP/0.9!s, + 'http 0.9 split between packets'); + +} + # spaces between digits not allowed since 1.29.1 like(http_get('/spaces'), qr!^HTTP/1.1 502 !s, 'status with spaces rejected'); @@ -157,6 +165,12 @@ sub http_daemon { print $client 'It is HTTP/0.9 response' . CRLF; + } elsif ($uri =~ m!/split!) { + + print $client 'HTTP'; + select undef, undef, undef, 0.1; + print $client '/0.9 split between packets'. CRLF; + } elsif ($uri =~ m!/spaces!) { print $client diff --git a/scgi_status.t b/scgi_status.t --- a/scgi_status.t +++ b/scgi_status.t @@ -25,7 +25,7 @@ eval { require SCGI; }; plan(skip_all => 'SCGI not installed') if $@; my $t = Test::Nginx->new() - ->has(qw/http scgi/)->plan(11) + ->has(qw/http scgi/)->plan(12) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -89,6 +89,18 @@ like(http_get('/001'), qr!^HTTP/1.1 502 } +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.1'); +todo_skip 'leaves coredump', 1 + unless $t->has_version('1.31.1') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/split'), qr!^HTTP/1.1 200 .*HTTP-Header: foo!s, + 'status line split between packets'); + +} + ############################################################################### sub scgi_daemon { @@ -146,6 +158,12 @@ sub scgi_daemon { } elsif ($uri eq '/001') { $c->print("Status: 001 Invalid\n\n"); + + } elsif ($uri eq '/split') { + $c->print("HTTP"); + select undef, undef, undef, 0.1; + $c->print("-Header: foo\n"); + $c->print("Status: 200 OK\n\n"); } } } From mdounin at mdounin.ru Sun May 24 21:30:26 2026 From: mdounin at mdounin.ru (=?iso-8859-1?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 00:30:26 +0300 Subject: [nginx-tests] Tests: improved ssl_verify_depth.t error reporting. Message-ID: details: http://freenginx.org/hg/nginx-tests/rev/2688ca1e99fb branches: changeset: 2060:2688ca1e99fb user: Maxim Dounin date: Sat May 23 19:44:39 2026 +0300 description: Tests: improved ssl_verify_depth.t error reporting. Previously, the test was skipped with LibreSSL due to issues with verify depth handling introduced in LibreSSL 3.4.0, and depth (mis)handling in OpenSSL 1.1.0+ was silently accepted. With this change, tests that are expected to fail with the particular library version are marked as TODO with an explanation. diffstat: ssl_verify_depth.t | 41 +++++++++++++++++++++++++++++++++++++---- 1 files changed, 37 insertions(+), 4 deletions(-) diffs (76 lines): diff --git a/ssl_verify_depth.t b/ssl_verify_depth.t --- a/ssl_verify_depth.t +++ b/ssl_verify_depth.t @@ -1,5 +1,6 @@ #!/usr/bin/perl +# (C) Maxim Dounin # (C) Sergey Kandaurov # (C) Nginx, Inc. @@ -25,8 +26,6 @@ select STDOUT; $| = 1; my $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/) ->has_daemon('openssl'); -plan(skip_all => 'LibreSSL') if $t->has_module('LibreSSL'); - $t->plan(9)->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -148,10 +147,28 @@ system("openssl ca -batch -config $d/ca. # as a result, it is not possible to limit certificate checking # to self-signed certificates only when using OpenSSL 1.1.0+ +# LibreSSL 3.4.0+ interprets verify depth 0 as no limit + like(get(8080, 'root'), qr/SUCCESS/, 'verify depth 0 - root'); -like(get(8080, 'int'), qr/FAI|SUC/, 'verify depth 0 - no int'); + +TODO: { +local $TODO = 'off-by-one depth in OpenSSL 1.1.0+' + if $t->has_feature('openssl:1.1.0'); +local $TODO = 'no zero depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0'); + +like(get(8080, 'int'), qr/FAILED/, 'verify depth 0 - no int'); + +} + +TODO: { +local $TODO = 'no zero depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0'); + like(get(8080, 'end'), qr/FAILED/, 'verify depth 0 - no end'); +} + # with verify depth 1 (the default), one signature is # expected to be checked, so certificates directly signed # by the root cert are allowed, but nothing more @@ -160,9 +177,25 @@ like(get(8080, 'end'), qr/FAILED/, 've # so with depth 1 it is possible to validate not only directly signed # certificates, but also chains with one intermediate certificate +# LibreSSL 3.4.0+ ignores depth limit as long as verify callback returns 1; +# fixed in LibreSSL 4.3.0, broken again in master after LibreSSL 4.3.1 +# (as seen on OpenBSD 7.9, with LibreSSL version reported as 4.3.0) + like(get(8081, 'root'), qr/SUCCESS/, 'verify depth 1 - root'); like(get(8081, 'int'), qr/SUCCESS/, 'verify depth 1 - int'); -like(get(8081, 'end'), qr/FAI|SUC/, 'verify depth 1 - no end'); + +TODO: { +local $TODO = 'off-by-one depth in OpenSSL 1.1.0+' + if $t->has_feature('openssl:1.1.0'); +local $TODO = 'ignored depth in LibreSSL 3.4.0+' + if $t->has_feature('libressl:3.4.0') + and not $t->has_feature('libressl:4.3.0') + or $t->has_feature('libressl:4.3.0') + and $^O eq 'openbsd'; + +like(get(8081, 'end'), qr/FAILED/, 'verify depth 1 - no end'); + +} # with verify depth 2 it is also possible to validate up to two signatures, # so chains with one intermediate certificate are allowed From mdounin at mdounin.ru Mon May 25 01:09:40 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:09:40 +0300 Subject: [PATCH 1 of 5] Version bump Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779656338 -10800 # Sun May 24 23:58:58 2026 +0300 # Node ID c0b692d54bb5058b6886b53dbadb6b97aa988050 # Parent d374a55efdb685aa57bdda116e72ecc8898d89b8 Version bump. diff --git a/src/core/nginx.h b/src/core/nginx.h --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1031001 -#define NGINX_VERSION "1.31.1" +#define nginx_version 1031002 +#define NGINX_VERSION "1.31.2" #define freenginx 1 From mdounin at mdounin.ru Mon May 25 01:09:41 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:09:41 +0300 Subject: [PATCH 2 of 5] Rewrite: removed optimized length calculations In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779656340 -10800 # Sun May 24 23:59:00 2026 +0300 # Node ID ebb65cf91321c0c58a8468e540dfebfdb8500322 # Parent c0b692d54bb5058b6886b53dbadb6b97aa988050 Rewrite: removed optimized length calculations. Previously, ngx_http_script_regex_start_code() tried to use optimized buffer length calculations when possible, without length codes evaluation. Originally the code assumed that allocating space for all the captures and escaping required for full URI is enough if variables are not used. In 641:5e8fb59c18c1 (0.3.42) this was further refined to require that duplicate captures are not used. However, length calculations can be wrong when nested captures are used, since the same URI character can appear in multiple captures and might require escaping in all of them, leading to a buffer overflow, for example (CVE-2026-9256): rewrite ^/((.*)) /?c=$1&d=$2; While it is possible to preserve and further refine optimized length calculations, it is believed that a better solution would be to remove them altogether, and this is what this change does. See also: https://github.com/nginx/nginx/commit/ca4f92a27464ae6c2082245e4f67048c633aa032 diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c --- a/src/http/modules/ngx_http_rewrite_module.c +++ b/src/http/modules/ngx_http_rewrite_module.c @@ -405,10 +405,6 @@ ngx_http_rewrite(ngx_conf_t *cf, ngx_com regex->size = sc.size; regex->args = sc.args; - if (sc.variables == 0 && !sc.dup_capture) { - regex->lengths = NULL; - } - regex_end = ngx_http_script_add_code(lcf->codes, sizeof(ngx_http_script_regex_end_code_t), ®ex); diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -483,12 +483,6 @@ ngx_http_script_compile(ngx_http_script_ n = sc->source->data[i] - '0'; - if (sc->captures_mask & ((ngx_uint_t) 1 << n)) { - sc->dup_capture = 1; - } - - sc->captures_mask |= (ngx_uint_t) 1 << n; - if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) { return NGX_ERROR; } @@ -1039,7 +1033,6 @@ ngx_http_script_regex_start_code(ngx_htt { size_t len; ngx_int_t rc; - ngx_uint_t n; ngx_http_request_t *r; ngx_http_script_engine_t le; ngx_http_script_len_code_pt lcode; @@ -1140,38 +1133,22 @@ ngx_http_script_regex_start_code(ngx_htt } } - if (code->lengths == NULL) { - e->buf.len = code->size; + ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); - if (code->uri) { - if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) { - e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, - NGX_ESCAPE_ARGS); - } - } - - for (n = 2; n < r->ncaptures; n += 2) { - e->buf.len += r->captures[n + 1] - r->captures[n]; - } + le.ip = code->lengths->elts; + le.line = e->line; + le.request = r; + le.quote = code->redirect; + le.is_args = e->is_args; - } else { - ngx_memzero(&le, sizeof(ngx_http_script_engine_t)); - - le.ip = code->lengths->elts; - le.line = e->line; - le.request = r; - le.quote = code->redirect; - le.is_args = e->is_args; + len = 0; - len = 0; + while (*(uintptr_t *) le.ip) { + lcode = *(ngx_http_script_len_code_pt *) le.ip; + len += lcode(&le); + } - while (*(uintptr_t *) le.ip) { - lcode = *(ngx_http_script_len_code_pt *) le.ip; - len += lcode(&le); - } - - e->buf.len = len; - } + e->buf.len = len; if (code->add_args && r->args.len) { e->buf.len += r->args.len + 1; diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h --- a/src/http/ngx_http_script.h +++ b/src/http/ngx_http_script.h @@ -46,7 +46,6 @@ typedef struct { ngx_uint_t variables; ngx_uint_t ncaptures; - ngx_uint_t captures_mask; ngx_uint_t size; void *main; @@ -58,7 +57,6 @@ typedef struct { unsigned conf_prefix:1; unsigned root_prefix:1; - unsigned dup_capture:1; unsigned args:1; } ngx_http_script_compile_t; From mdounin at mdounin.ru Mon May 25 01:09:42 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:09:42 +0300 Subject: [PATCH 3 of 5] Rewrite: fixed "if" with relative file names In-Reply-To: References: Message-ID: <81500b5d5a1bcc8c89b3.1779671382@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1779656347 -10800 # Sun May 24 23:59:07 2026 +0300 # Node ID 81500b5d5a1bcc8c89b3f192577dfd65512fad2c # Parent ebb65cf91321c0c58a8468e540dfebfdb8500322 Rewrite: fixed "if" with relative file names. Previously, relative file names in "if" were not specifically handled and therefore resolved from the process current working directory, leading to changes in behaviour depending on the current directory during startup. This also differs from the expected behaviour of paths used in the configuration, which are expected to be resolved either from prefix or from configuration prefix. The fix is to use ngx_get_full_name() in ngx_http_script_file_code(), so relative paths, if used, are resolved from prefix. diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -1606,6 +1606,14 @@ ngx_http_script_file_code(ngx_http_scrip r = e->request; + if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path) + != NGX_OK) + { + e->ip = ngx_http_script_exit; + e->status = NGX_HTTP_INTERNAL_SERVER_ERROR; + return; + } + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http script file op %p \"%V\"", (void *) code->op, &path); From mdounin at mdounin.ru Mon May 25 01:09:43 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:09:43 +0300 Subject: [PATCH 4 of 5] Script: removed unneeded full name length code In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779656354 -10800 # Sun May 24 23:59:14 2026 +0300 # Node ID c058606a02aed53f52b26479897a35c0dbdb2f59 # Parent 81500b5d5a1bcc8c89b3f192577dfd65512fad2c Script: removed unneeded full name length code. Allocating additional buffer space for prefix is not needed, since relative paths are expanded using the ngx_get_full_name() function, which does an allocation itself when needed. diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -23,7 +23,6 @@ static ngx_int_t ngx_http_script_add_cap #endif static ngx_int_t ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc); -static size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e); static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e); @@ -1395,17 +1394,6 @@ ngx_http_script_add_full_name_code(ngx_h { ngx_http_script_full_name_code_t *code; - code = ngx_http_script_add_code(*sc->lengths, - sizeof(ngx_http_script_full_name_code_t), - NULL); - if (code == NULL) { - return NGX_ERROR; - } - - code->code = (ngx_http_script_code_pt) (uintptr_t) - ngx_http_script_full_name_len_code; - code->conf_prefix = sc->conf_prefix; - code = ngx_http_script_add_code(*sc->values, sizeof(ngx_http_script_full_name_code_t), &sc->main); @@ -1420,20 +1408,6 @@ ngx_http_script_add_full_name_code(ngx_h } -static size_t -ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e) -{ - ngx_http_script_full_name_code_t *code; - - code = (ngx_http_script_full_name_code_t *) e->ip; - - e->ip += sizeof(ngx_http_script_full_name_code_t); - - return code->conf_prefix ? ngx_cycle->conf_prefix.len: - ngx_cycle->prefix.len; -} - - static void ngx_http_script_full_name_code(ngx_http_script_engine_t *e) { diff --git a/src/stream/ngx_stream_script.c b/src/stream/ngx_stream_script.c --- a/src/stream/ngx_stream_script.c +++ b/src/stream/ngx_stream_script.c @@ -23,8 +23,6 @@ static ngx_int_t ngx_stream_script_add_c #endif static ngx_int_t ngx_stream_script_add_full_name_code( ngx_stream_script_compile_t *sc); -static size_t ngx_stream_script_full_name_len_code( - ngx_stream_script_engine_t *e); static void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e); @@ -952,17 +950,6 @@ ngx_stream_script_add_full_name_code(ngx { ngx_stream_script_full_name_code_t *code; - code = ngx_stream_script_add_code(*sc->lengths, - sizeof(ngx_stream_script_full_name_code_t), - NULL); - if (code == NULL) { - return NGX_ERROR; - } - - code->code = (ngx_stream_script_code_pt) (uintptr_t) - ngx_stream_script_full_name_len_code; - code->conf_prefix = sc->conf_prefix; - code = ngx_stream_script_add_code(*sc->values, sizeof(ngx_stream_script_full_name_code_t), &sc->main); if (code == NULL) { @@ -976,20 +963,6 @@ ngx_stream_script_add_full_name_code(ngx } -static size_t -ngx_stream_script_full_name_len_code(ngx_stream_script_engine_t *e) -{ - ngx_stream_script_full_name_code_t *code; - - code = (ngx_stream_script_full_name_code_t *) e->ip; - - e->ip += sizeof(ngx_stream_script_full_name_code_t); - - return code->conf_prefix ? ngx_cycle->conf_prefix.len: - ngx_cycle->prefix.len; -} - - static void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e) { From mdounin at mdounin.ru Mon May 25 01:09:44 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:09:44 +0300 Subject: [PATCH 5 of 5] Script: switched to ngx_align() macro in code size calculations In-Reply-To: References: Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779656364 -10800 # Sun May 24 23:59:24 2026 +0300 # Node ID ca6f420a4edd9fd334e11d87540c0f899bc6f04f # Parent c058606a02aed53f52b26479897a35c0dbdb2f59 Script: switched to ngx_align() macro in code size calculations. Previously, explicitly written alignment operations were used. With the ngx_align() macro the code becomes slightly more readable, and script code size calculations for values better match ones for lengths. No functional changes. 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 @@ -3473,9 +3473,8 @@ ngx_http_fastcgi_init_params(ngx_conf_t copy->len = src[i].skip_empty; - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_http_script_copy_code_t) + + ngx_align(src[i].key.len, sizeof(uintptr_t)); copy = ngx_array_push_n(params->values, size); if (copy == NULL) { 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 @@ -4094,9 +4094,8 @@ ngx_http_proxy_init_headers(ngx_conf_t * ngx_http_script_copy_len_code; copy->len = src[i].key.len; - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_http_script_copy_code_t) + + ngx_align(src[i].key.len, sizeof(uintptr_t)); copy = ngx_array_push_n(headers->values, size); if (copy == NULL) { 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 @@ -1840,9 +1840,8 @@ ngx_http_scgi_init_params(ngx_conf_t *cf copy->len = src[i].skip_empty; - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + 1 + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_http_script_copy_code_t) + + ngx_align(src[i].key.len + 1, sizeof(uintptr_t)); copy = ngx_array_push_n(params->values, size); if (copy == NULL) { 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 @@ -2141,9 +2141,8 @@ ngx_http_uwsgi_init_params(ngx_conf_t *c copy->len = src[i].skip_empty; - size = (sizeof(ngx_http_script_copy_code_t) - + src[i].key.len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_http_script_copy_code_t) + + ngx_align(src[i].key.len, sizeof(uintptr_t)); copy = ngx_array_push_n(params->values, size); if (copy == NULL) { diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c --- a/src/http/ngx_http_script.c +++ b/src/http/ngx_http_script.c @@ -194,12 +194,10 @@ ngx_http_compile_complex_value(ngx_http_ return NGX_ERROR; } - n = (nv * (2 * sizeof(ngx_http_script_copy_code_t) - + sizeof(ngx_http_script_var_code_t)) - + sizeof(uintptr_t) - + v->len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + n = nv * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t) + + ngx_align(v->len, sizeof(uintptr_t)); if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { return NGX_ERROR; @@ -696,12 +694,10 @@ ngx_http_script_init_arrays(ngx_http_scr } if (*sc->values == NULL) { - n = (sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) - + sizeof(ngx_http_script_var_code_t)) - + sizeof(uintptr_t) - + sc->source->len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t) + + sizeof(ngx_http_script_var_code_t)) + + sizeof(uintptr_t) + + ngx_align(sc->source->len, sizeof(uintptr_t)); *sc->values = ngx_array_create(sc->cf->pool, n, 1); if (*sc->values == NULL) { @@ -819,8 +815,8 @@ ngx_http_script_add_copy_code(ngx_http_s ngx_http_script_copy_len_code; code->len = len; - size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_http_script_copy_code_t) + + ngx_align(len, sizeof(uintptr_t)); code = ngx_http_script_add_code(*sc->values, size, &sc->main); if (code == NULL) { @@ -871,7 +867,7 @@ ngx_http_script_copy_code(ngx_http_scrip } e->ip += sizeof(ngx_http_script_copy_code_t) - + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); + + ngx_align(code->len, sizeof(uintptr_t)); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0, "http script copy: \"%*s\"", e->pos - p, p); diff --git a/src/stream/ngx_stream_script.c b/src/stream/ngx_stream_script.c --- a/src/stream/ngx_stream_script.c +++ b/src/stream/ngx_stream_script.c @@ -194,12 +194,10 @@ ngx_stream_compile_complex_value(ngx_str return NGX_ERROR; } - n = (nv * (2 * sizeof(ngx_stream_script_copy_code_t) - + sizeof(ngx_stream_script_var_code_t)) - + sizeof(uintptr_t) - + v->len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + n = nv * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + ngx_align(v->len, sizeof(uintptr_t)); if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) { return NGX_ERROR; @@ -577,12 +575,10 @@ ngx_stream_script_init_arrays(ngx_stream } if (*sc->values == NULL) { - n = (sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) - + sizeof(ngx_stream_script_var_code_t)) - + sizeof(uintptr_t) - + sc->source->len - + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + n = sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t) + + sizeof(ngx_stream_script_var_code_t)) + + sizeof(uintptr_t) + + ngx_align(sc->source->len, sizeof(uintptr_t)); *sc->values = ngx_array_create(sc->cf->pool, n, 1); if (*sc->values == NULL) { @@ -688,8 +684,8 @@ ngx_stream_script_add_copy_code(ngx_stre ngx_stream_script_copy_len_code; code->len = len; - size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1) - & ~(sizeof(uintptr_t) - 1); + size = sizeof(ngx_stream_script_copy_code_t) + + ngx_align(len, sizeof(uintptr_t)); code = ngx_stream_script_add_code(*sc->values, size, &sc->main); if (code == NULL) { @@ -740,7 +736,7 @@ ngx_stream_script_copy_code(ngx_stream_s } e->ip += sizeof(ngx_stream_script_copy_code_t) - + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1)); + + ngx_align(code->len, sizeof(uintptr_t)); ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0, "stream script copy: \"%*s\"", e->pos - p, p); From mdounin at mdounin.ru Mon May 25 01:11:50 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:11:50 +0300 Subject: [PATCH] Tests: added rewrite test with nested captures In-Reply-To: References: Message-ID: <48645a6fd3936888837a.1779671510@vm-bsd.mdounin.ru> # HG changeset patch # User Maxim Dounin # Date 1779658307 -10800 # Mon May 25 00:31:47 2026 +0300 # Node ID 48645a6fd3936888837abcc3e0edc3a2e65047ca # Parent 2688ca1e99fb3b56e8837c7d45c23e2f1c9eacdd Tests: added rewrite test with nested captures. diff --git a/rewrite.t b/rewrite.t --- a/rewrite.t +++ b/rewrite.t @@ -21,7 +21,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(24) +my $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(25) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -137,6 +137,11 @@ http { return 200 "uri:$uri args:$args"; } + location /capture_nested { + rewrite ^((.*)) /?c=$1&d=$2; + return 200 "uri:$uri args:$args"; + } + location /break { rewrite ^ /return200; break; @@ -256,6 +261,19 @@ like(http_get('/capture_another/%25?a=b' } +TODO: { +local $TODO = 'not yet' + unless $t->has_version('1.31.2'); +todo_skip 'might coredump', 1 + unless $t->has_version('1.31.2') + or $ENV{TEST_NGINX_UNSAFE}; + +like(http_get('/capture_nested/%25?a=b'), + qr!^uri:/ args:c=/capture_nested/%25&d=/capture_nested/%25&a=b$!ms, + 'escape with nested captures'); + +} + # break like(http_get('/break'), qr/200/, 'valid_location reset'); From mdounin at mdounin.ru Mon May 25 01:13:52 2026 From: mdounin at mdounin.ru (=?utf-8?q?Maxim_Dounin?=) Date: Mon, 25 May 2026 04:13:52 +0300 Subject: [PATCH] Tests: added tests for relative file names in "if" In-Reply-To: <81500b5d5a1bcc8c89b3.1779671382@vm-bsd.mdounin.ru> References: <81500b5d5a1bcc8c89b3.1779671382@vm-bsd.mdounin.ru> Message-ID: # HG changeset patch # User Maxim Dounin # Date 1779658309 -10800 # Mon May 25 00:31:49 2026 +0300 # Node ID b9f33520b42cafbd084483f88a1fc9fb513592a9 # Parent 48645a6fd3936888837abcc3e0edc3a2e65047ca Tests: added tests for relative file names in "if". diff --git a/rewrite_if.t b/rewrite_if.t --- a/rewrite_if.t +++ b/rewrite_if.t @@ -22,7 +22,7 @@ use Test::Nginx; select STDERR; $| = 1; select STDOUT; $| = 1; -my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(33) +my $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(35) ->write_file_expand('nginx.conf', <<'EOF'); %%TEST_GLOBALS%% @@ -134,6 +134,18 @@ http { return 204; } } + + location /relative { + if (-f file) { + return 204; + } + } + + location /relative_var { + if (-f $arg_c) { + return 204; + } + } } } @@ -192,4 +204,12 @@ unlike(http_get('/not_exec?c=dir'), qr/ } +TODO: { +local $TODO = 'not yet' unless $t->has_version('1.31.2'); + +like(http_get('/relative'), qr/ 204 /, 'relative file name'); +like(http_get('/relative_var?c=file'), qr/ 204 /, 'relative file name var'); + +} + ###############################################################################