From 16ebfa999baac42341da6589ffaa8550b25acb2e Mon Sep 17 00:00:00 2001
From: Roman Arutyunyan <arut@nginx.com>
Date: Mon, 3 Jun 2019 20:33:26 +0300
Subject: [PATCH 01/33] Upstream: background cache update before cache send
 (ticket #1782).

In case of filter finalization, essential request fields like r->uri,
r->args etc could be changed, which affected the cache update subrequest.
Also, after filter finalization r->cache could be set to NULL, leading to
null pointer dereference in ngx_http_upstream_cache_background_update().
The fix is to create background cache update subrequest before sending the
cached response.

Since initial introduction in 1aeaae6e9446 (1.11.10) background cache update
subrequest was created after sending the cached response because otherwise it
blocked the parent request output.  In 9552758a786e (1.13.1) background
subrequests were introduced to eliminate the delay before sending the final
part of the cached response.  This also made it possible to create the
background cache update subrequest before sending the response.

Note that creating the subrequest earlier does not change the fact that in case
of filter finalization the background cache update subrequest will likely not
have enough time to successfully update the cache entry.  Filter finalization
leads to the main request termination as soon the current iteration of request
processing is complete.
---
 src/http/ngx_http_upstream.c | 19 ++++++++-----------
 1 file changed, 8 insertions(+), 11 deletions(-)

diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index 925824366..ffb09a91f 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -597,10 +597,6 @@ ngx_http_upstream_init_request(ngx_http_request_t *r)
                 u->cache_status = NGX_HTTP_CACHE_MISS;
                 u->request_sent = 1;
             }
-
-            if (ngx_http_upstream_cache_background_update(r, u) != NGX_OK) {
-                rc = NGX_ERROR;
-            }
         }
 
         if (rc != NGX_DECLINED) {
@@ -902,9 +898,14 @@ ngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)
              || c->stale_updating) && !r->background
             && u->conf->cache_background_update)
         {
-            r->cache->background = 1;
-            u->cache_status = rc;
-            rc = NGX_OK;
+            if (ngx_http_upstream_cache_background_update(r, u) == NGX_OK) {
+                r->cache->background = 1;
+                u->cache_status = rc;
+                rc = NGX_OK;
+
+            } else {
+                rc = NGX_ERROR;
+            }
         }
 
         break;
@@ -1106,10 +1107,6 @@ ngx_http_upstream_cache_background_update(ngx_http_request_t *r,
 {
     ngx_http_request_t  *sr;
 
-    if (!r->cached || !r->cache->background) {
-        return NGX_OK;
-    }
-
     if (r == r->main) {
         r->preserve_body = 1;
     }
-- 
GitLab


From dda58fc63b64227a19657ed92168b9f645c0c86e Mon Sep 17 00:00:00 2001
From: Roman Arutyunyan <arut@nginx.com>
Date: Wed, 5 Jun 2019 19:55:27 +0300
Subject: [PATCH 02/33] Limit req: limit_req_dry_run directive.

A new directive limit_req_dry_run allows enabling the dry run mode.  In this
mode requests are neither rejected nor delayed, but reject/delay status is
logged as usual.
---
 src/http/modules/ngx_http_limit_req_module.c | 29 +++++++++++++++++---
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/http/modules/ngx_http_limit_req_module.c b/src/http/modules/ngx_http_limit_req_module.c
index e81d57ffb..806452296 100644
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -53,6 +53,7 @@ typedef struct {
     ngx_uint_t                   limit_log_level;
     ngx_uint_t                   delay_log_level;
     ngx_uint_t                   status_code;
+    ngx_flag_t                   dry_run;
 } ngx_http_limit_req_conf_t;
 
 
@@ -118,6 +119,13 @@ static ngx_command_t  ngx_http_limit_req_commands[] = {
       offsetof(ngx_http_limit_req_conf_t, status_code),
       &ngx_http_limit_req_status_bounds },
 
+    { ngx_string("limit_req_dry_run"),
+      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_limit_req_conf_t, dry_run),
+      NULL },
+
       ngx_null_command
 };
 
@@ -230,9 +238,10 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
 
         if (rc == NGX_BUSY) {
             ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
-                          "limiting requests, excess: %ui.%03ui by zone \"%V\"",
-                          excess / 1000, excess % 1000,
-                          &limit->shm_zone->shm.name);
+                        "limiting requests%s, excess: %ui.%03ui by zone \"%V\"",
+                        lrcf->dry_run ? ", dry run" : "",
+                        excess / 1000, excess % 1000,
+                        &limit->shm_zone->shm.name);
         }
 
         while (n--) {
@@ -251,6 +260,10 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
             ctx->node = NULL;
         }
 
+        if (lrcf->dry_run) {
+            return NGX_DECLINED;
+        }
+
         return lrcf->status_code;
     }
 
@@ -267,9 +280,14 @@ ngx_http_limit_req_handler(ngx_http_request_t *r)
     }
 
     ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
-                  "delaying request, excess: %ui.%03ui, by zone \"%V\"",
+                  "delaying request%s, excess: %ui.%03ui, by zone \"%V\"",
+                  lrcf->dry_run ? ", dry run" : "",
                   excess / 1000, excess % 1000, &limit->shm_zone->shm.name);
 
+    if (lrcf->dry_run) {
+        return NGX_DECLINED;
+    }
+
     if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
@@ -711,6 +729,7 @@ ngx_http_limit_req_create_conf(ngx_conf_t *cf)
 
     conf->limit_log_level = NGX_CONF_UNSET_UINT;
     conf->status_code = NGX_CONF_UNSET_UINT;
+    conf->dry_run = NGX_CONF_UNSET;
 
     return conf;
 }
@@ -735,6 +754,8 @@ ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)
     ngx_conf_merge_uint_value(conf->status_code, prev->status_code,
                               NGX_HTTP_SERVICE_UNAVAILABLE);
 
+    ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);
+
     return NGX_CONF_OK;
 }
 
-- 
GitLab


From d9887ee2ae9069843eed67d5b5ea625a7faeedb1 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Mon, 17 Jun 2019 19:48:56 +0300
Subject: [PATCH 03/33] Perl: disabled not_modified filter (ticket #1786).

Embedded perl does not set any request fields needed for conditional
requests processing.  Further, filter finalization in the not_modified
filter can cause segmentation faults due to cleared ctx as in
ticket #1786.

Before 5fb1e57c758a (1.7.3) the not_modified filter was implicitly disabled
for perl responses, as r->headers_out.last_modified_time was -1.  This
change restores this behaviour by using the explicit r->disable_not_modified
flag.

Note that this patch doesn't try to address perl module robustness against
filter finalization and other errors returned from filter chains.  It should
be eventually reworked to handle errors instead of ignoring them.
---
 src/http/modules/perl/nginx.xs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index ad1263287..848e50199 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -147,6 +147,8 @@ send_http_header(r, ...)
         }
     }
 
+    r->disable_not_modified = 1;
+
     (void) ngx_http_send_header(r);
 
 
-- 
GitLab


From 32adc8534c4e461a1e519ee58e1a846249743872 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 25 Jun 2019 04:47:43 +0300
Subject: [PATCH 04/33] Updated OpenSSL used for win32 builds.

---
 misc/GNUmakefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/GNUmakefile b/misc/GNUmakefile
index 0391a3155..2a34ae553 100644
--- a/misc/GNUmakefile
+++ b/misc/GNUmakefile
@@ -6,7 +6,7 @@ TEMP =		tmp
 
 CC =		cl
 OBJS =		objs.msvc8
-OPENSSL =	openssl-1.1.1b
+OPENSSL =	openssl-1.1.1c
 ZLIB =		zlib-1.2.11
 PCRE =		pcre-8.43
 
-- 
GitLab


From c4613796af6102f5c6b2365f660ac4591808832b Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 25 Jun 2019 15:19:45 +0300
Subject: [PATCH 05/33] nginx-1.17.1-RELEASE

---
 docs/xml/nginx/changes.xml | 56 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index ae034bbaa..5963cd7d7 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,62 @@
 <change_log title="nginx">
 
 
+<changes ver="1.17.1" date="2019-06-25">
+
+<change type="feature">
+<para lang="ru">
+Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° limit_req_dry_run.
+</para>
+<para lang="en">
+the "limit_req_dry_run" directive.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ hash Š² Š±Š»Š¾ŠŗŠµ upstream
+ŠæустŠ¾Š¹ ŠŗŠ»ŃˇŃ‡ хѨшŠøŃ€Š¾Š²Š°Š½ŠøѸ Ń‚ŠµŠæŠµŃ€ŃŒ ŠæŃ€ŠøŠ²Š¾Š´ŠøŃ‚ Šŗ ŠæŠµŃ€ŠµŠŗŠ»ŃˇŃ‡ŠµŠ½Šøѡ
+Š½Š° round-robin Š±Š°Š»Š°Š½ŃŠøŃ€Š¾Š²Šŗу.<br/>
+Š�ŠæŠ°ŃŠøŠ±Š¾ Niklas Keller.
+</para>
+<para lang="en">
+when using the "hash" directive inside the "upstream" block
+an empty hash key now triggers round-robin balancing.<br/>
+Thanks to Niklas Keller.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Ń€Š°Š±Š¾Ń‡ŠµŠ¼ ŠæŃ€Š¾Ń†ŠµŃŃŠµ Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
+ŠµŃŠ»Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š»Š¾ŃŃŒ ŠŗѨшŠøŃ€Š¾Š²Š°Š½ŠøŠµ Šø Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° image_filter,
+Š° Š¾ŃˆŠøŠ±ŠŗŠø с ŠŗŠ¾Š´Š¾Š¼ 415 ŠæŠµŃ€ŠµŠ½Š°ŠæŃ€Š°Š²Š»Ń¸Š»Šøсь с ŠæŠ¾Š¼Š¾Ń‰ŃŒ Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ error_page;
+Š¾ŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Ń¸Š²ŠøŠ»Š°ŃŃŒ Š² 1.11.10.
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process
+if caching was used along with the "image_filter" directive,
+and errors with code 415 were redirected with the "error_page" directive;
+the bug had appeared in 1.11.10.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Ń€Š°Š±Š¾Ń‡ŠµŠ¼ ŠæŃ€Š¾Ń†ŠµŃŃŠµ Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
+ŠµŃŠ»Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š»ŃŃ¸ Š²ŃŃ‚Ń€Š¾ŠµŠ½Š½Ń‹Š¹ ŠæŠµŃ€Š»;
+Š¾ŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Ń¸Š²ŠøŠ»Š°ŃŃŒ Š² 1.7.3.
+</para>
+<para lang="en">
+a segmentation fault might occur in a worker process
+if embedded perl was used;
+the bug had appeared in 1.7.3.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.17.0" date="2019-05-21">
 
 <change type="feature">
-- 
GitLab


From 4722452e9cb42f1e0456da7c3d86808471c556b5 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 25 Jun 2019 15:19:45 +0300
Subject: [PATCH 06/33] release-1.17.1 tag

---
 .hgtags | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.hgtags b/.hgtags
index 5dd625bb4..10adccb73 100644
--- a/.hgtags
+++ b/.hgtags
@@ -439,3 +439,4 @@ d2fd76709909767fc727a5b4affcf1dc9ca488a7 release-1.15.9
 5155d0296a5ef9841f035920527ffdb771076b44 release-1.15.11
 0130ca3d58437b3c7c707cdddd813d530c68da9a release-1.15.12
 054c1c46395caff79bb4caf16f40b331f71bb6dd release-1.17.0
+7816bd7dabf6ee86c53c073b90a7143161546e06 release-1.17.1
-- 
GitLab


From 66e95b50e7e65dc16dcf2a5ca51219af3d4c8d9f Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 9 Jul 2019 16:01:32 +0300
Subject: [PATCH 07/33] Version bump.

---
 src/core/nginx.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/nginx.h b/src/core/nginx.h
index 71377bf16..25c5a729f 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1017001
-#define NGINX_VERSION      "1.17.1"
+#define nginx_version      1017002
+#define NGINX_VERSION      "1.17.2"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
-- 
GitLab


From 18e063129a75d7bdb0e87dc982ab43a48bf9cf28 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 9 Jul 2019 16:03:25 +0300
Subject: [PATCH 08/33] Typo.

---
 docs/xml/nginx/changes.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 5963cd7d7..60221ab55 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -34,7 +34,7 @@ Thanks to Niklas Keller.
 <para lang="ru">
 Š² Ń€Š°Š±Š¾Ń‡ŠµŠ¼ ŠæŃ€Š¾Ń†ŠµŃŃŠµ Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
 ŠµŃŠ»Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š»Š¾ŃŃŒ ŠŗѨшŠøŃ€Š¾Š²Š°Š½ŠøŠµ Šø Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Š° image_filter,
-Š° Š¾ŃˆŠøŠ±ŠŗŠø с ŠŗŠ¾Š´Š¾Š¼ 415 ŠæŠµŃ€ŠµŠ½Š°ŠæŃ€Š°Š²Š»Ń¸Š»Šøсь с ŠæŠ¾Š¼Š¾Ń‰ŃŒ Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ error_page;
+Š° Š¾ŃˆŠøŠ±ŠŗŠø с ŠŗŠ¾Š´Š¾Š¼ 415 ŠæŠµŃ€ŠµŠ½Š°ŠæŃ€Š°Š²Š»Ń¸Š»Šøсь с ŠæŠ¾Š¼Š¾Ń‰ŃŒŃˇ Š´ŠøŃ€ŠµŠŗŃ‚ŠøŠ²Ń‹ error_page;
 Š¾ŃˆŠøŠ±ŠŗŠ° ŠæŠ¾Ń¸Š²ŠøŠ»Š°ŃŃŒ Š² 1.11.10.
 </para>
 <para lang="en">
-- 
GitLab


From 1e673c72dd9d0e88ec5918622e9656fe46b02403 Mon Sep 17 00:00:00 2001
From: Gena Makhomed <gmm@csdoc.com>
Date: Sun, 30 Jun 2019 10:39:01 +0300
Subject: [PATCH 09/33] Contrib: vim syntax, update core and 3rd party module
 directives.

---
 contrib/vim/syntax/nginx.vim | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/contrib/vim/syntax/nginx.vim b/contrib/vim/syntax/nginx.vim
index 6bee7a2e5..1a3a7b7d9 100644
--- a/contrib/vim/syntax/nginx.vim
+++ b/contrib/vim/syntax/nginx.vim
@@ -333,6 +333,7 @@ syn keyword ngxDirective contained js_access
 syn keyword ngxDirective contained js_content
 syn keyword ngxDirective contained js_filter
 syn keyword ngxDirective contained js_include
+syn keyword ngxDirective contained js_path
 syn keyword ngxDirective contained js_preread
 syn keyword ngxDirective contained js_set
 syn keyword ngxDirective contained keepalive
@@ -353,6 +354,7 @@ syn keyword ngxDirective contained limit_conn_zone
 syn keyword ngxDirective contained limit_rate
 syn keyword ngxDirective contained limit_rate_after
 syn keyword ngxDirective contained limit_req
+syn keyword ngxDirective contained limit_req_dry_run
 syn keyword ngxDirective contained limit_req_log_level
 syn keyword ngxDirective contained limit_req_status
 syn keyword ngxDirective contained limit_req_zone
@@ -472,6 +474,7 @@ syn keyword ngxDirective contained proxy_requests
 syn keyword ngxDirective contained proxy_responses
 syn keyword ngxDirective contained proxy_send_lowat
 syn keyword ngxDirective contained proxy_send_timeout
+syn keyword ngxDirective contained proxy_session_drop
 syn keyword ngxDirective contained proxy_set_body
 syn keyword ngxDirective contained proxy_set_header
 syn keyword ngxDirective contained proxy_socket_keepalive
@@ -1325,6 +1328,7 @@ syn keyword ngxDirectiveThirdParty contained lua_check_client_abort
 syn keyword ngxDirectiveThirdParty contained lua_code_cache
 syn keyword ngxDirectiveThirdParty contained lua_fake_shm
 syn keyword ngxDirectiveThirdParty contained lua_http10_buffering
+syn keyword ngxDirectiveThirdParty contained lua_load_resty_core
 syn keyword ngxDirectiveThirdParty contained lua_malloc_trim
 syn keyword ngxDirectiveThirdParty contained lua_max_pending_timers
 syn keyword ngxDirectiveThirdParty contained lua_max_running_timers
@@ -1779,6 +1783,7 @@ syn keyword ngxDirectiveThirdParty contained vod_expires_live_time_dependent
 syn keyword ngxDirectiveThirdParty contained vod_fallback_upstream_location
 syn keyword ngxDirectiveThirdParty contained vod_force_continuous_timestamps
 syn keyword ngxDirectiveThirdParty contained vod_force_playlist_type_vod
+syn keyword ngxDirectiveThirdParty contained vod_force_sequence_index
 syn keyword ngxDirectiveThirdParty contained vod_gop_look_ahead
 syn keyword ngxDirectiveThirdParty contained vod_gop_look_behind
 syn keyword ngxDirectiveThirdParty contained vod_ignore_edit_list
-- 
GitLab


From 60e7480533480b6340cae596118aa0ac72c0fce9 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 11 Jul 2019 23:20:08 +0300
Subject: [PATCH 10/33] Perl: removed unneeded NGX_DONE test.

The NGX_DONE test in ngx_http_perl_handle_request() was introduced
in 1702:86bb52e28ce0, which also modified ngx_http_perl_call_handler()
to return NGX_DONE with c->destroyed.  The latter part was then
removed in 3050:f54b02dbb12b, so NGX_DONE test is no longer needed.
---
 src/http/modules/perl/ngx_http_perl_module.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index 6d3be9128..d06b43c75 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -228,11 +228,6 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "perl handler done: %i", rc);
 
-    if (rc == NGX_DONE) {
-        ngx_http_finalize_request(r, rc);
-        return;
-    }
-
     if (rc > 600) {
         rc = NGX_OK;
     }
-- 
GitLab


From eae5e4dd01dfaff9d15c3dd7818f082e2995cc74 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 11:29:22 +0300
Subject: [PATCH 11/33] Perl: reworked perl module to pass ctx instead of
 request.

This ensures that correct ctx is always available, including after
filter finalization.  In particular, this fixes a segmentation fault
with the following configuration:

    location / {
        image_filter test;

        perl 'sub {
            my $r = shift;
            $r->send_http_header();
            $r->print("foo\n");
            $r->print("bar\n");
        }';
    }

This also seems to be the only way to correctly handle filter finalization
in various complex cases, for example, when embedded perl is used both
in the original handler and in an error page called after filter
finalization.
---
 src/http/modules/perl/nginx.xs               | 180 ++++++++++---------
 src/http/modules/perl/ngx_http_perl_module.c |  26 ++-
 src/http/modules/perl/ngx_http_perl_module.h |   2 +
 3 files changed, 114 insertions(+), 94 deletions(-)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 848e50199..2e9808f2b 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -15,8 +15,10 @@
 #include "XSUB.h"
 
 
-#define ngx_http_perl_set_request(r)                                          \
-    r = INT2PTR(ngx_http_request_t *, SvIV((SV *) SvRV(ST(0))))
+#define ngx_http_perl_set_request(r, ctx)                                     \
+                                                                              \
+    ctx = INT2PTR(ngx_http_perl_ctx_t *, SvIV((SV *) SvRV(ST(0))));           \
+    r = ctx->request
 
 
 #define ngx_http_perl_set_targ(p, len)                                        \
@@ -64,14 +66,12 @@ ngx_http_perl_sv2str(pTHX_ ngx_http_request_t *r, ngx_str_t *s, SV *sv)
 
 
 static ngx_int_t
-ngx_http_perl_output(ngx_http_request_t *r, ngx_buf_t *b)
+ngx_http_perl_output(ngx_http_request_t *r, ngx_http_perl_ctx_t *ctx,
+    ngx_buf_t *b)
 {
-    ngx_chain_t           out;
+    ngx_chain_t   out;
 #if (NGX_HTTP_SSI)
-    ngx_chain_t          *cl;
-    ngx_http_perl_ctx_t  *ctx;
-
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
+    ngx_chain_t  *cl;
 
     if (ctx->ssi) {
         cl = ngx_alloc_chain_link(r->pool);
@@ -105,9 +105,10 @@ void
 status(r, code)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     r->headers_out.status = SvIV(ST(1));
 
@@ -121,10 +122,11 @@ void
 send_http_header(r, ...)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *sv;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *sv;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
@@ -157,9 +159,10 @@ header_only(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     sv_upgrade(TARG, SVt_IV);
     sv_setiv(TARG, r->header_only);
@@ -172,9 +175,10 @@ uri(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->uri.data, r->uri.len);
 
     ST(0) = TARG;
@@ -185,9 +189,10 @@ args(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->args.data, r->args.len);
 
     ST(0) = TARG;
@@ -198,9 +203,10 @@ request_method(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->method_name.data, r->method_name.len);
 
     ST(0) = TARG;
@@ -211,9 +217,10 @@ remote_addr(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
     ngx_http_perl_set_targ(r->connection->addr_text.data,
                            r->connection->addr_text.len);
 
@@ -226,6 +233,7 @@ header_in(r, key)
 
     dXSTARG;
     ngx_http_request_t         *r;
+    ngx_http_perl_ctx_t        *ctx;
     SV                         *key;
     u_char                     *p, *lowcase_key, *value, sep;
     STRLEN                      len;
@@ -237,7 +245,7 @@ header_in(r, key)
     ngx_http_header_t          *hh;
     ngx_http_core_main_conf_t  *cmcf;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     key = ST(1);
 
@@ -374,13 +382,12 @@ has_request_body(r, next)
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
         XSRETURN_UNDEF;
     }
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
     ctx->next = SvRV(ST(1));
 
     r->request_body_in_single_buf = 1;
@@ -404,13 +411,14 @@ request_body(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
-    u_char              *p, *data;
-    size_t               len;
-    ngx_buf_t           *buf;
-    ngx_chain_t         *cl;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    u_char               *p, *data;
+    size_t                len;
+    ngx_buf_t            *buf;
+    ngx_chain_t          *cl;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->request_body == NULL
         || r->request_body->temp_file
@@ -465,9 +473,10 @@ request_body_file(r)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (r->request_body == NULL || r->request_body->temp_file == NULL) {
         XSRETURN_UNDEF;
@@ -483,9 +492,10 @@ void
 discard_request_body(r)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     ngx_http_discard_request_body(r);
 
@@ -494,12 +504,13 @@ void
 header_out(r, key, value)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *key;
-    SV                  *value;
-    ngx_table_elt_t     *header;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *key;
+    SV                   *value;
+    ngx_table_elt_t      *header;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     key = ST(1);
     value = ST(2);
@@ -542,13 +553,12 @@ filename(r)
     CODE:
 
     dXSTARG;
-    size_t                root;
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
+    size_t                root;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
     if (ctx->filename.data) {
         goto done;
     }
@@ -571,15 +581,16 @@ void
 print(r, ...)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *sv;
-    int                  i;
-    u_char              *p;
-    size_t               size;
-    STRLEN               len;
-    ngx_buf_t           *b;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *sv;
+    int                   i;
+    u_char               *p;
+    size_t                size;
+    STRLEN                len;
+    ngx_buf_t            *b;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     if (items == 2) {
 
@@ -660,7 +671,7 @@ print(r, ...)
 
     out:
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
 
 void
@@ -668,6 +679,7 @@ sendfile(r, filename, offset = -1, bytes = 0)
     CODE:
 
     ngx_http_request_t        *r;
+    ngx_http_perl_ctx_t       *ctx;
     char                      *filename;
     off_t                      offset;
     size_t                     bytes;
@@ -676,7 +688,7 @@ sendfile(r, filename, offset = -1, bytes = 0)
     ngx_open_file_info_t       of;
     ngx_http_core_loc_conf_t  *clcf;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     filename = SvPV_nolen(ST(1));
 
@@ -750,17 +762,18 @@ sendfile(r, filename, offset = -1, bytes = 0)
     b->file->log = r->connection->log;
     b->file->directio = of.is_directio;
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
 
 void
 flush(r)
     CODE:
 
-    ngx_http_request_t  *r;
-    ngx_buf_t           *b;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    ngx_buf_t            *b;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
@@ -771,7 +784,7 @@ flush(r)
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->flush");
 
-    (void) ngx_http_perl_output(r, b);
+    (void) ngx_http_perl_output(r, ctx, b);
 
     XSRETURN_EMPTY;
 
@@ -781,16 +794,14 @@ internal_redirect(r, uri)
     CODE:
 
     ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
     SV                   *uri;
     ngx_uint_t            i;
-    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     uri = ST(1);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
     if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
         XSRETURN_EMPTY;
     }
@@ -811,9 +822,10 @@ void
 allow_ranges(r)
     CODE:
 
-    ngx_http_request_t  *r;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     r->allow_ranges = 1;
 
@@ -823,13 +835,14 @@ unescape(r, text, type = 0)
     CODE:
 
     dXSTARG;
-    ngx_http_request_t  *r;
-    SV                  *text;
-    int                  type;
-    u_char              *p, *dst, *src;
-    STRLEN               len;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *text;
+    int                   type;
+    u_char               *p, *dst, *src;
+    STRLEN                len;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     text = ST(1);
 
@@ -858,16 +871,16 @@ variable(r, name, value = NULL)
 
     dXSTARG;
     ngx_http_request_t         *r;
+    ngx_http_perl_ctx_t        *ctx;
     SV                         *name, *value;
     u_char                     *p, *lowcase;
     STRLEN                      len;
     ngx_str_t                   var, val;
     ngx_uint_t                  i, hash;
     ngx_http_perl_var_t        *v;
-    ngx_http_perl_ctx_t        *ctx;
     ngx_http_variable_value_t  *vv;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     name = ST(1);
 
@@ -919,8 +932,6 @@ variable(r, name, value = NULL)
 
     if (vv->not_found) {
 
-        ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
         if (ctx->variables) {
 
             v = ctx->variables->elts;
@@ -991,18 +1002,16 @@ sleep(r, sleep, next)
     CODE:
 
     ngx_http_request_t   *r;
-    ngx_msec_t            sleep;
     ngx_http_perl_ctx_t  *ctx;
+    ngx_msec_t            sleep;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     sleep = (ngx_msec_t) SvIV(ST(1));
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "perl sleep: %M", sleep);
 
-    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);
-
     ctx->next = SvRV(ST(2));
 
     r->connection->write->delayed = 1;
@@ -1016,13 +1025,14 @@ void
 log_error(r, err, msg)
     CODE:
 
-    ngx_http_request_t  *r;
-    SV                  *err, *msg;
-    u_char              *p;
-    STRLEN               len;
-    ngx_err_t            e;
+    ngx_http_request_t   *r;
+    ngx_http_perl_ctx_t  *ctx;
+    SV                   *err, *msg;
+    u_char               *p;
+    STRLEN                len;
+    ngx_err_t             e;
 
-    ngx_http_perl_set_request(r);
+    ngx_http_perl_set_request(r, ctx);
 
     err = ST(1);
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index d06b43c75..c2ef47048 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -43,7 +43,8 @@ static PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,
 static ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,
     ngx_log_t *log);
 static ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
-    HV *nginx, SV *sub, SV **args, ngx_str_t *handler, ngx_str_t *rv);
+    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,
+    ngx_str_t *handler, ngx_str_t *rv);
 static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);
 
 static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);
@@ -199,6 +200,8 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -220,8 +223,8 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
         ctx->next = NULL;
     }
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sub, NULL, handler,
-                                    NULL);
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sub, NULL,
+                                    handler, NULL);
 
     }
 
@@ -309,6 +312,8 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -321,7 +326,7 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     PERL_SET_CONTEXT(pmcf->perl);
     PERL_SET_INTERP(pmcf->perl);
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, pv->sub, NULL,
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, pv->sub, NULL,
                                     &pv->handler, &value);
 
     }
@@ -372,6 +377,8 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
         }
 
         ngx_http_set_ctx(r, ctx, ngx_http_perl_module);
+
+        ctx->request = r;
     }
 
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
@@ -430,8 +437,8 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
         asv = NULL;
     }
 
-    rc = ngx_http_perl_call_handler(aTHX_ r, pmcf->nginx, sv, asv, handler,
-                                    NULL);
+    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sv, asv,
+                                    handler, NULL);
 
     SvREFCNT_dec(sv);
 
@@ -667,8 +674,9 @@ ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)
 
 
 static ngx_int_t
-ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,
-    SV **args, ngx_str_t *handler, ngx_str_t *rv)
+ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
+    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,
+    ngx_str_t *handler, ngx_str_t *rv)
 {
     SV                *sv;
     int                n, status;
@@ -687,7 +695,7 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r, HV *nginx, SV *sub,
 
     PUSHMARK(sp);
 
-    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(r))), nginx));
+    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(ctx))), nginx));
     XPUSHs(sv);
 
     if (args) {
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index 5e60b031e..4f1eaa3a2 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -21,6 +21,8 @@
 typedef ngx_http_request_t   *nginx;
 
 typedef struct {
+    ngx_http_request_t       *request;
+
     ngx_str_t                 filename;
     ngx_str_t                 redirect_uri;
     ngx_str_t                 redirect_args;
-- 
GitLab


From 4a0771f9a62eccf168e651a502e67ec17d1cd6c7 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 13:56:21 +0300
Subject: [PATCH 12/33] Perl: propagate errors.

When an error happens, the ctx->error bit is now set, and croak()
is called to terminate further processing.  The ctx->error bit is
checked in ngx_http_perl_call_handler() to cancel further processing,
and is also checked in various output functions - to make sure these won't
be called if croak() was handled by an eval{} in perl code.

In particular, this ensures that output chain won't be called after
errors, as filters might not expect this to happen.  This fixes some
segmentation faults under low memory conditions.  Also this stops
request processing after filter finalization or request body reading
errors.

For cases where an HTTP error status can be additionally returned (for
example, 416 (Requested Range Not Satisfiable) from the range filter),
the ctx->status field is also added.
---
 src/http/modules/perl/nginx.xs               | 72 ++++++++++++++++++--
 src/http/modules/perl/ngx_http_perl_module.c | 20 ++++++
 src/http/modules/perl/ngx_http_perl_module.h |  5 +-
 3 files changed, 90 insertions(+), 7 deletions(-)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 2e9808f2b..8e17f6d57 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -125,9 +125,14 @@ send_http_header(r, ...)
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
     SV                   *sv;
+    ngx_int_t             rc;
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->error) {
+        croak("send_http_header(): called after error");
+    }
+
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
     }
@@ -151,7 +156,13 @@ send_http_header(r, ...)
 
     r->disable_not_modified = 1;
 
-    (void) ngx_http_send_header(r);
+    rc = ngx_http_send_header(r);
+
+    if (rc == NGX_ERROR || rc > NGX_OK) {
+        ctx->error = 1;
+        ctx->status = rc;
+        croak("ngx_http_send_header() failed");
+    }
 
 
 void
@@ -381,6 +392,7 @@ has_request_body(r, next)
     dXSTARG;
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
+    ngx_int_t             rc;
 
     ngx_http_perl_set_request(r, ctx);
 
@@ -398,7 +410,14 @@ has_request_body(r, next)
         r->request_body_file_log_level = 0;
     }
 
-    ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
+    rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);
+
+    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
+        ctx->error = 1;
+        ctx->status = rc;
+        ctx->next = NULL;
+        croak("ngx_http_read_client_request_body() failed");
+    }
 
     sv_upgrade(TARG, SVt_IV);
     sv_setiv(TARG, 1);
@@ -494,10 +513,17 @@ discard_request_body(r)
 
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
+    ngx_int_t             rc;
 
     ngx_http_perl_set_request(r, ctx);
 
-    ngx_http_discard_request_body(r);
+    rc = ngx_http_discard_request_body(r);
+
+    if (rc != NGX_OK) {
+        ctx->error = 1;
+        ctx->status = rc;
+        croak("ngx_http_discard_request_body() failed");
+    }
 
 
 void
@@ -512,6 +538,10 @@ header_out(r, key, value)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->error) {
+        croak("header_out(): called after error");
+    }
+
     key = ST(1);
     value = ST(2);
 
@@ -588,10 +618,15 @@ print(r, ...)
     u_char               *p;
     size_t                size;
     STRLEN                len;
+    ngx_int_t             rc;
     ngx_buf_t            *b;
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->error) {
+        croak("print(): called after error");
+    }
+
     if (items == 2) {
 
         /*
@@ -671,7 +706,12 @@ print(r, ...)
 
     out:
 
-    (void) ngx_http_perl_output(r, ctx, b);
+    rc = ngx_http_perl_output(r, ctx, b);
+
+    if (rc == NGX_ERROR) {
+        ctx->error = 1;
+        croak("ngx_http_perl_output() failed");
+    }
 
 
 void
@@ -683,6 +723,7 @@ sendfile(r, filename, offset = -1, bytes = 0)
     char                      *filename;
     off_t                      offset;
     size_t                     bytes;
+    ngx_int_t                  rc;
     ngx_str_t                  path;
     ngx_buf_t                 *b;
     ngx_open_file_info_t       of;
@@ -690,6 +731,10 @@ sendfile(r, filename, offset = -1, bytes = 0)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->error) {
+        croak("sendfile(): called after error");
+    }
+
     filename = SvPV_nolen(ST(1));
 
     if (filename == NULL) {
@@ -762,7 +807,12 @@ sendfile(r, filename, offset = -1, bytes = 0)
     b->file->log = r->connection->log;
     b->file->directio = of.is_directio;
 
-    (void) ngx_http_perl_output(r, ctx, b);
+    rc = ngx_http_perl_output(r, ctx, b);
+
+    if (rc == NGX_ERROR) {
+        ctx->error = 1;
+        croak("ngx_http_perl_output() failed");
+    }
 
 
 void
@@ -771,10 +821,15 @@ flush(r)
 
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
+    ngx_int_t             rc;
     ngx_buf_t            *b;
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->error) {
+        croak("flush(): called after error");
+    }
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
         XSRETURN_EMPTY;
@@ -784,7 +839,12 @@ flush(r)
 
     ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "$r->flush");
 
-    (void) ngx_http_perl_output(r, ctx, b);
+    rc = ngx_http_perl_output(r, ctx, b);
+
+    if (rc == NGX_ERROR) {
+        ctx->error = 1;
+        croak("ngx_http_perl_output() failed");
+    }
 
     XSRETURN_EMPTY;
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index c2ef47048..ac6a7a2a3 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -246,6 +246,11 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
     ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
 
+    if (rc == NGX_ERROR) {
+        ngx_http_finalize_request(r, rc);
+        return;
+    }
+
     if (ctx->done || ctx->next) {
         ngx_http_finalize_request(r, NGX_DONE);
         return;
@@ -690,6 +695,9 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
 
     status = 0;
 
+    ctx->error = 0;
+    ctx->status = NGX_OK;
+
     ENTER;
     SAVETMPS;
 
@@ -739,6 +747,18 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
     FREETMPS;
     LEAVE;
 
+    if (ctx->error) {
+
+        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
+                       "call_sv: error, %d", ctx->status);
+
+        if (ctx->status != NGX_OK) {
+            return ctx->status;
+        }
+
+        return NGX_ERROR;
+    }
+
     /* check $@ */
 
     if (SvTRUE(ERRSV)) {
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index 4f1eaa3a2..5c967dfb3 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -29,7 +29,10 @@ typedef struct {
 
     SV                       *next;
 
-    ngx_uint_t                done;       /* unsigned  done:1; */
+    ngx_int_t                 status;
+
+    unsigned                  done:1;
+    unsigned                  error:1;
 
     ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */
 
-- 
GitLab


From 9d266efbc0c1170215397ae669165911b5a828d6 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 13:56:23 +0300
Subject: [PATCH 13/33] Perl: handling of allocation errors.

Previously, allocation errors in nginx.xs were more or less ignored,
potentially resulting in incorrect code execution in specific low-memory
conditions.  This is changed to use ctx->error bit and croak(), similarly
to how output errors are now handled.

Note that this is mostly a cosmetic change, as Perl itself exits on memory
allocation errors, and hence nginx with Perl is hardly usable in low-memory
conditions.
---
 src/http/modules/perl/nginx.xs | 76 +++++++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 25 deletions(-)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 8e17f6d57..104def1e8 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -143,14 +143,16 @@ send_http_header(r, ...)
         if (ngx_http_perl_sv2str(aTHX_ r, &r->headers_out.content_type, sv)
             != NGX_OK)
         {
-            XSRETURN_EMPTY;
+            ctx->error = 1;
+            croak("ngx_http_perl_sv2str() failed");
         }
 
         r->headers_out.content_type_len = r->headers_out.content_type.len;
 
     } else {
         if (ngx_http_set_content_type(r) != NGX_OK) {
-            XSRETURN_EMPTY;
+            ctx->error = 1;
+            croak("ngx_http_set_content_type() failed");
         }
     }
 
@@ -270,7 +272,8 @@ header_in(r, key)
 
     lowcase_key = ngx_pnalloc(r->pool, len);
     if (lowcase_key == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_pnalloc() failed");
     }
 
     hash = ngx_hash_strlow(lowcase_key, p, len);
@@ -330,7 +333,8 @@ header_in(r, key)
 
         value = ngx_pnalloc(r->pool, size);
         if (value == NULL) {
-            XSRETURN_UNDEF;
+            ctx->error = 1;
+            croak("ngx_pnalloc() failed");
         }
 
         p = value;
@@ -465,7 +469,8 @@ request_body(r)
 
     p = ngx_pnalloc(r->pool, len);
     if (p == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_pnalloc() failed");
     }
 
     data = p;
@@ -547,19 +552,22 @@ header_out(r, key, value)
 
     header = ngx_list_push(&r->headers_out.headers);
     if (header == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_list_push() failed");
     }
 
     header->hash = 1;
 
     if (ngx_http_perl_sv2str(aTHX_ r, &header->key, key) != NGX_OK) {
         header->hash = 0;
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_http_perl_sv2str() failed");
     }
 
     if (ngx_http_perl_sv2str(aTHX_ r, &header->value, value) != NGX_OK) {
         header->hash = 0;
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_http_perl_sv2str() failed");
     }
 
     if (header->key.len == sizeof("Content-Length") - 1
@@ -594,7 +602,8 @@ filename(r)
     }
 
     if (ngx_http_map_uri_to_path(r, &ctx->filename, &root, 0) == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_http_map_uri_to_path() failed");
     }
 
     ctx->filename.len--;
@@ -650,7 +659,8 @@ print(r, ...)
 
             b = ngx_calloc_buf(r->pool);
             if (b == NULL) {
-                XSRETURN_EMPTY;
+                ctx->error = 1;
+                croak("ngx_calloc_buf() failed");
             }
 
             b->memory = 1;
@@ -690,7 +700,8 @@ print(r, ...)
 
     b = ngx_create_temp_buf(r->pool, size);
     if (b == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_create_temp_buf() failed");
     }
 
     for (i = 1; i < items; i++) {
@@ -746,19 +757,22 @@ sendfile(r, filename, offset = -1, bytes = 0)
 
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_calloc_buf() failed");
     }
 
     b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
     if (b->file == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_pcalloc() failed");
     }
 
     path.len = ngx_strlen(filename);
 
     path.data = ngx_pnalloc(r->pool, path.len + 1);
     if (path.data == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_pnalloc() failed");
     }
 
     (void) ngx_cpystrn(path.data, (u_char *) filename, path.len + 1);
@@ -775,19 +789,23 @@ sendfile(r, filename, offset = -1, bytes = 0)
     of.events = clcf->open_file_cache_events;
 
     if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_http_set_disable_symlinks() failed");
     }
 
     if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
         != NGX_OK)
     {
         if (of.err == 0) {
-            XSRETURN_EMPTY;
+            ctx->error = 1;
+            croak("ngx_open_cached_file() failed");
         }
 
         ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
                       "%s \"%s\" failed", of.failed, filename);
-        XSRETURN_EMPTY;
+
+        ctx->error = 1;
+        croak("ngx_open_cached_file() failed");
     }
 
     if (offset == -1) {
@@ -832,7 +850,8 @@ flush(r)
 
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_calloc_buf() failed");
     }
 
     b->flush = 1;
@@ -863,7 +882,8 @@ internal_redirect(r, uri)
     uri = ST(1);
 
     if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
-        XSRETURN_EMPTY;
+        ctx->error = 1;
+        croak("ngx_http_perl_sv2str() failed");
     }
 
     for (i = 0; i < ctx->redirect_uri.len; i++) {
@@ -910,7 +930,8 @@ unescape(r, text, type = 0)
 
     p = ngx_pnalloc(r->pool, len + 1);
     if (p == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_pnalloc() failed");
     }
 
     dst = p;
@@ -959,7 +980,8 @@ variable(r, name, value = NULL)
         }
 
         if (ngx_http_perl_sv2str(aTHX_ r, &val, value) != NGX_OK) {
-            XSRETURN_UNDEF;
+            ctx->error = 1;
+            croak("ngx_http_perl_sv2str() failed");
         }
     }
 
@@ -967,7 +989,8 @@ variable(r, name, value = NULL)
 
     lowcase = ngx_pnalloc(r->pool, len);
     if (lowcase == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_pnalloc() failed");
     }
 
     hash = ngx_hash_strlow(lowcase, p, len);
@@ -987,7 +1010,8 @@ variable(r, name, value = NULL)
 
     vv = ngx_http_get_variable(r, &var, hash);
     if (vv == NULL) {
-        XSRETURN_UNDEF;
+        ctx->error = 1;
+        croak("ngx_http_get_variable() failed");
     }
 
     if (vv->not_found) {
@@ -1020,13 +1044,15 @@ variable(r, name, value = NULL)
                 ctx->variables = ngx_array_create(r->pool, 1,
                                                   sizeof(ngx_http_perl_var_t));
                 if (ctx->variables == NULL) {
-                    XSRETURN_UNDEF;
+                    ctx->error = 1;
+                    croak("ngx_array_create() failed");
                 }
             }
 
             v = ngx_array_push(ctx->variables);
             if (v == NULL) {
-                XSRETURN_UNDEF;
+                ctx->error = 1;
+                croak("ngx_array_push() failed");
             }
 
             v->hash = hash;
-- 
GitLab


From 19887831698e18149a45a8b9563e8fdcdaaea211 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:34:37 +0300
Subject: [PATCH 14/33] Perl: protection against duplicate $r->sleep() calls.

Duplicate $r->sleep() and/or $r->has_request_body() calls result
in undefined behaviour (in practice, connection leaks were observed).
To prevent this, croak() added in appropriate places.
---
 src/http/modules/perl/nginx.xs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 104def1e8..e9db2f7d6 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -400,6 +400,10 @@ has_request_body(r, next)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->next) {
+        croak("has_request_body(): another handler active");
+    }
+
     if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
         XSRETURN_UNDEF;
     }
@@ -1093,6 +1097,10 @@ sleep(r, sleep, next)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->next) {
+        croak("sleep(): another handler active");
+    }
+
     sleep = (ngx_msec_t) SvIV(ST(1));
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-- 
GitLab


From cae2e689083a04b2ddbb93bef5313b136080df65 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:35:31 +0300
Subject: [PATCH 15/33] Perl: disabled unrelated calls from variable handlers.

Variable handlers are not expected to send anything to the client, cannot
sleep or read body, and are not expected to modify the request.  Added
appropriate protection to prevent accidental foot shooting.
---
 src/http/modules/perl/nginx.xs               | 44 ++++++++++++++++++++
 src/http/modules/perl/ngx_http_perl_module.c |  5 +++
 src/http/modules/perl/ngx_http_perl_module.h |  1 +
 3 files changed, 50 insertions(+)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index e9db2f7d6..67ec0a564 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -110,6 +110,10 @@ status(r, code)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("status(): cannot be used in variable handler");
+    }
+
     r->headers_out.status = SvIV(ST(1));
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -133,6 +137,10 @@ send_http_header(r, ...)
         croak("send_http_header(): called after error");
     }
 
+    if (ctx->variable) {
+        croak("send_http_header(): cannot be used in variable handler");
+    }
+
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
     }
@@ -400,6 +408,10 @@ has_request_body(r, next)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("has_request_body(): cannot be used in variable handler");
+    }
+
     if (ctx->next) {
         croak("has_request_body(): another handler active");
     }
@@ -526,6 +538,10 @@ discard_request_body(r)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("discard_request_body(): cannot be used in variable handler");
+    }
+
     rc = ngx_http_discard_request_body(r);
 
     if (rc != NGX_OK) {
@@ -551,6 +567,10 @@ header_out(r, key, value)
         croak("header_out(): called after error");
     }
 
+    if (ctx->variable) {
+        croak("header_out(): cannot be used in variable handler");
+    }
+
     key = ST(1);
     value = ST(2);
 
@@ -640,6 +660,10 @@ print(r, ...)
         croak("print(): called after error");
     }
 
+    if (ctx->variable) {
+        croak("print(): cannot be used in variable handler");
+    }
+
     if (items == 2) {
 
         /*
@@ -750,6 +774,10 @@ sendfile(r, filename, offset = -1, bytes = 0)
         croak("sendfile(): called after error");
     }
 
+    if (ctx->variable) {
+        croak("sendfile(): cannot be used in variable handler");
+    }
+
     filename = SvPV_nolen(ST(1));
 
     if (filename == NULL) {
@@ -852,6 +880,10 @@ flush(r)
         croak("flush(): called after error");
     }
 
+    if (ctx->variable) {
+        croak("flush(): cannot be used in variable handler");
+    }
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
         ctx->error = 1;
@@ -883,6 +915,10 @@ internal_redirect(r, uri)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("internal_redirect(): cannot be used in variable handler");
+    }
+
     uri = ST(1);
 
     if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
@@ -911,6 +947,10 @@ allow_ranges(r)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("allow_ranges(): cannot be used in variable handler");
+    }
+
     r->allow_ranges = 1;
 
 
@@ -1097,6 +1137,10 @@ sleep(r, sleep, next)
 
     ngx_http_perl_set_request(r, ctx);
 
+    if (ctx->variable) {
+        croak("sleep(): cannot be used in variable handler");
+    }
+
     if (ctx->next) {
         croak("sleep(): another handler active");
     }
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index ac6a7a2a3..a383e9002 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -302,6 +302,7 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
 
     ngx_int_t                   rc;
     ngx_str_t                   value;
+    ngx_uint_t                  saved;
     ngx_http_perl_ctx_t        *ctx;
     ngx_http_perl_main_conf_t  *pmcf;
 
@@ -321,6 +322,9 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
         ctx->request = r;
     }
 
+    saved = ctx->variable;
+    ctx->variable = 1;
+
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
 
     value.data = NULL;
@@ -347,6 +351,7 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,
         v->not_found = 1;
     }
 
+    ctx->variable = saved;
     ctx->filename.data = NULL;
     ctx->redirect_uri.len = 0;
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index 5c967dfb3..b67ce137c 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -33,6 +33,7 @@ typedef struct {
 
     unsigned                  done:1;
     unsigned                  error:1;
+    unsigned                  variable:1;
 
     ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */
 
-- 
GitLab


From 12d6b3b4a1d024bd8098e20b43b0b7020e5286e6 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:38:27 +0300
Subject: [PATCH 16/33] Perl: avoid redirects on errors.

Previously, redirects scheduled with $r->internal_redirect() were followed
even if the code then died.  Now these are ignored and nginx will return
an error instead.
---
 src/http/modules/perl/ngx_http_perl_module.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index a383e9002..81b2526ad 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -778,6 +778,8 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
             return NGX_ERROR;
         }
 
+        ctx->redirect_uri.len = 0;
+
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-- 
GitLab


From 78b39bd631fc18fd5778090183776f5275005e21 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:39:25 +0300
Subject: [PATCH 17/33] Perl: avoid returning 500 if header was already sent.

Returning NGX_HTTP_INTERNAL_SERVER_ERROR if a perl code died after
sending header will lead to a "header already sent" alert.  To avoid
it, we now check if header was already sent, and return NGX_ERROR
instead if it was.
---
 src/http/modules/perl/nginx.xs               | 2 ++
 src/http/modules/perl/ngx_http_perl_module.c | 4 ++++
 src/http/modules/perl/ngx_http_perl_module.h | 1 +
 3 files changed, 7 insertions(+)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 67ec0a564..34ce9daed 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -164,6 +164,8 @@ send_http_header(r, ...)
         }
     }
 
+    ctx->header_sent = 1;
+
     r->disable_not_modified = 1;
 
     rc = ngx_http_send_header(r);
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index 81b2526ad..dc2125935 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -780,6 +780,10 @@ ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,
 
         ctx->redirect_uri.len = 0;
 
+        if (ctx->header_sent) {
+            return NGX_ERROR;
+        }
+
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index b67ce137c..da3a16235 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -34,6 +34,7 @@ typedef struct {
     unsigned                  done:1;
     unsigned                  error:1;
     unsigned                  variable:1;
+    unsigned                  header_sent:1;
 
     ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */
 
-- 
GitLab


From 9e883a2e48ff8e55fcfb091284b44d8fa66fc007 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:39:25 +0300
Subject: [PATCH 18/33] Perl: additional ctx->header_sent checks.

As we now have ctx->header_sent flag, it is further used to prevent
duplicate $r->send_http_header() calls, prevent output before sending
header, and $r->internal_redirect() after sending header.

Further, $r->send_http_header() protected from calls after
$r->internal_redirect().
---
 src/http/modules/perl/nginx.xs               | 24 ++++++++++++++++++++
 src/http/modules/perl/ngx_http_perl_module.c |  1 +
 2 files changed, 25 insertions(+)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 34ce9daed..7370bf5fc 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -141,6 +141,14 @@ send_http_header(r, ...)
         croak("send_http_header(): cannot be used in variable handler");
     }
 
+    if (ctx->header_sent) {
+        croak("send_http_header(): header already sent");
+    }
+
+    if (ctx->redirect_uri.len) {
+        croak("send_http_header(): cannot be used with internal_redirect()");
+    }
+
     if (r->headers_out.status == 0) {
         r->headers_out.status = NGX_HTTP_OK;
     }
@@ -666,6 +674,10 @@ print(r, ...)
         croak("print(): cannot be used in variable handler");
     }
 
+    if (!ctx->header_sent) {
+        croak("print(): header not sent");
+    }
+
     if (items == 2) {
 
         /*
@@ -780,6 +792,10 @@ sendfile(r, filename, offset = -1, bytes = 0)
         croak("sendfile(): cannot be used in variable handler");
     }
 
+    if (!ctx->header_sent) {
+        croak("sendfile(): header not sent");
+    }
+
     filename = SvPV_nolen(ST(1));
 
     if (filename == NULL) {
@@ -886,6 +902,10 @@ flush(r)
         croak("flush(): cannot be used in variable handler");
     }
 
+    if (!ctx->header_sent) {
+        croak("flush(): header not sent");
+    }
+
     b = ngx_calloc_buf(r->pool);
     if (b == NULL) {
         ctx->error = 1;
@@ -921,6 +941,10 @@ internal_redirect(r, uri)
         croak("internal_redirect(): cannot be used in variable handler");
     }
 
+    if (ctx->header_sent) {
+        croak("internal_redirect(): header already sent");
+    }
+
     uri = ST(1);
 
     if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index dc2125935..d2a0dfae5 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -394,6 +394,7 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,
     pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);
 
     ctx->ssi = ssi_ctx;
+    ctx->header_sent = 1;
 
     handler = params[NGX_HTTP_PERL_SSI_SUB];
     handler->data[handler->len] = '\0';
-- 
GitLab


From 8df08b02b89c151e4bf04bc3c7c9a37e9ebcba9d Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:39:26 +0300
Subject: [PATCH 19/33] Perl: expect escaped URIs in $r->internal_redirect().

Similarly to the change in 5491:74bfa803a5aa (1.5.9), we should accept
properly escaped URIs and unescape them as needed, else it is not possible
to handle URIs with question marks.
---
 src/http/modules/perl/nginx.xs               | 11 -----------
 src/http/modules/perl/ngx_http_perl_module.c | 10 +++++++++-
 src/http/modules/perl/ngx_http_perl_module.h |  1 -
 3 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 7370bf5fc..309b7cbfb 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -952,17 +952,6 @@ internal_redirect(r, uri)
         croak("ngx_http_perl_sv2str() failed");
     }
 
-    for (i = 0; i < ctx->redirect_uri.len; i++) {
-        if (ctx->redirect_uri.data[i] == '?') {
-
-            ctx->redirect_args.len = ctx->redirect_uri.len - (i + 1);
-            ctx->redirect_args.data = &ctx->redirect_uri.data[i + 1];
-            ctx->redirect_uri.len = i;
-
-            XSRETURN_EMPTY;
-        }
-    }
-
 
 void
 allow_ranges(r)
diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index d2a0dfae5..a123e3875 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -184,6 +184,7 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
     SV                         *sub;
     ngx_int_t                   rc;
     ngx_str_t                   uri, args, *handler;
+    ngx_uint_t                  flags;
     ngx_http_perl_ctx_t        *ctx;
     ngx_http_perl_loc_conf_t   *plcf;
     ngx_http_perl_main_conf_t  *pmcf;
@@ -237,7 +238,6 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
 
     if (ctx->redirect_uri.len) {
         uri = ctx->redirect_uri;
-        args = ctx->redirect_args;
 
     } else {
         uri.len = 0;
@@ -257,6 +257,14 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
     }
 
     if (uri.len) {
+        ngx_str_null(&args);
+        flags = NGX_HTTP_LOG_UNSAFE;
+
+        if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
+            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+            return;
+        }
+
         ngx_http_internal_redirect(r, &uri, &args);
         ngx_http_finalize_request(r, NGX_DONE);
         return;
diff --git a/src/http/modules/perl/ngx_http_perl_module.h b/src/http/modules/perl/ngx_http_perl_module.h
index da3a16235..8fa59a4c5 100644
--- a/src/http/modules/perl/ngx_http_perl_module.h
+++ b/src/http/modules/perl/ngx_http_perl_module.h
@@ -25,7 +25,6 @@ typedef struct {
 
     ngx_str_t                 filename;
     ngx_str_t                 redirect_uri;
-    ngx_str_t                 redirect_args;
 
     SV                       *next;
 
-- 
GitLab


From 29fea7d9ec7b18d9f3c2e77bddd873dafbd10842 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 12 Jul 2019 15:39:28 +0300
Subject: [PATCH 20/33] Perl: named locations in $r->internal_redirect().

---
 src/http/modules/perl/ngx_http_perl_module.c | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c
index a123e3875..f3fc62947 100644
--- a/src/http/modules/perl/ngx_http_perl_module.c
+++ b/src/http/modules/perl/ngx_http_perl_module.c
@@ -257,15 +257,21 @@ ngx_http_perl_handle_request(ngx_http_request_t *r)
     }
 
     if (uri.len) {
-        ngx_str_null(&args);
-        flags = NGX_HTTP_LOG_UNSAFE;
+        if (uri.data[0] == '@') {
+            ngx_http_named_location(r, &uri);
 
-        if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
-            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
-            return;
+        } else {
+            ngx_str_null(&args);
+            flags = NGX_HTTP_LOG_UNSAFE;
+
+            if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
+                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
+                return;
+            }
+
+            ngx_http_internal_redirect(r, &uri, &args);
         }
 
-        ngx_http_internal_redirect(r, &uri, &args);
         ngx_http_finalize_request(r, NGX_DONE);
         return;
     }
-- 
GitLab


From cfa1316368dcc6dc1aa82e3d0b67ec0d1cf7eebb Mon Sep 17 00:00:00 2001
From: Ilya Leoshkevich <iii@linux.ibm.com>
Date: Fri, 12 Jul 2019 12:43:08 +0200
Subject: [PATCH 21/33] Gzip: use zlib to write header and trailer.

When nginx is used with zlib patched with [1], which provides
integration with the future IBM Z hardware deflate acceleration, it ends
up computing CRC32 twice: one time in hardware, which always does this,
and one time in software by explicitly calling crc32().

crc32() calls were added in changesets 133:b27548f540ad ("nginx-0.0.1-
2003-09-24-23:51:12 import") and 134:d57c6835225c ("nginx-0.0.1-
2003-09-26-09:45:21 import") as part of gzip wrapping feature - back
then zlib did not support it.

However, since then gzip wrapping was implemented in zlib v1.2.0.4,
and it's already being used by nginx for log compression.

This patch replaces hand-written gzip wrapping with the one provided by
zlib. It simplifies the code, and makes it avoid computing CRC32 twice
when using hardware acceleration.

[1] https://github.com/madler/zlib/pull/410
---
 .../modules/ngx_http_gzip_filter_module.c     | 124 +-----------------
 1 file changed, 7 insertions(+), 117 deletions(-)

diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c
index e4c343c9b..48f3dd7c7 100644
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -55,44 +55,23 @@ typedef struct {
     unsigned             redo:1;
     unsigned             done:1;
     unsigned             nomem:1;
-    unsigned             gzheader:1;
     unsigned             buffering:1;
     unsigned             intel:1;
 
     size_t               zin;
     size_t               zout;
 
-    uint32_t             crc32;
     z_stream             zstream;
     ngx_http_request_t  *request;
 } ngx_http_gzip_ctx_t;
 
 
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-struct gztrailer {
-    uint32_t  crc32;
-    uint32_t  zlen;
-};
-
-#else /* NGX_HAVE_BIG_ENDIAN || !NGX_HAVE_NONALIGNED */
-
-struct gztrailer {
-    u_char  crc32[4];
-    u_char  zlen[4];
-};
-
-#endif
-
-
 static void ngx_http_gzip_filter_memory(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx);
 static ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,
     ngx_chain_t *in);
 static ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx);
-static ngx_int_t ngx_http_gzip_filter_gzheader(ngx_http_request_t *r,
-    ngx_http_gzip_ctx_t *ctx);
 static ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx);
 static ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,
@@ -446,12 +425,6 @@ ngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
             return ctx->busy ? NGX_AGAIN : NGX_OK;
         }
 
-        if (!ctx->gzheader) {
-            if (ngx_http_gzip_filter_gzheader(r, ctx) != NGX_OK) {
-                goto failed;
-            }
-        }
-
         rc = ngx_http_next_body_filter(r, ctx->out);
 
         if (rc == NGX_ERROR) {
@@ -643,7 +616,7 @@ ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
     ctx->zstream.opaque = ctx;
 
     rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,
-                      - ctx->wbits, ctx->memlevel, Z_DEFAULT_STRATEGY);
+                      ctx->wbits + 16, ctx->memlevel, Z_DEFAULT_STRATEGY);
 
     if (rc != Z_OK) {
         ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
@@ -652,45 +625,12 @@ ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,
     }
 
     ctx->last_out = &ctx->out;
-    ctx->crc32 = crc32(0L, Z_NULL, 0);
     ctx->flush = Z_NO_FLUSH;
 
     return NGX_OK;
 }
 
 
-static ngx_int_t
-ngx_http_gzip_filter_gzheader(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
-{
-    ngx_buf_t      *b;
-    ngx_chain_t    *cl;
-    static u_char  gzheader[10] =
-                               { 0x1f, 0x8b, Z_DEFLATED, 0, 0, 0, 0, 0, 0, 3 };
-
-    b = ngx_calloc_buf(r->pool);
-    if (b == NULL) {
-        return NGX_ERROR;
-    }
-
-    b->memory = 1;
-    b->pos = gzheader;
-    b->last = b->pos + 10;
-
-    cl = ngx_alloc_chain_link(r->pool);
-    if (cl == NULL) {
-        return NGX_ERROR;
-    }
-
-    cl->buf = b;
-    cl->next = ctx->out;
-    ctx->out = cl;
-
-    ctx->gzheader = 1;
-
-    return NGX_OK;
-}
-
-
 static ngx_int_t
 ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
 {
@@ -743,14 +683,9 @@ ngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
 
     } else if (ctx->in_buf->flush) {
         ctx->flush = Z_SYNC_FLUSH;
-    }
-
-    if (ctx->zstream.avail_in) {
 
-        ctx->crc32 = crc32(ctx->crc32, ctx->zstream.next_in,
-                           ctx->zstream.avail_in);
-
-    } else if (ctx->flush == Z_NO_FLUSH) {
+    } else if (ctx->zstream.avail_in == 0) {
+        /* ctx->flush == Z_NO_FLUSH */
         return NGX_AGAIN;
     }
 
@@ -932,13 +867,11 @@ static ngx_int_t
 ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx)
 {
-    int                rc;
-    ngx_buf_t         *b;
-    ngx_chain_t       *cl;
-    struct gztrailer  *trailer;
+    int           rc;
+    ngx_chain_t  *cl;
 
     ctx->zin = ctx->zstream.total_in;
-    ctx->zout = 10 + ctx->zstream.total_out + 8;
+    ctx->zout = ctx->zstream.total_out;
 
     rc = deflateEnd(&ctx->zstream);
 
@@ -960,50 +893,7 @@ ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
     *ctx->last_out = cl;
     ctx->last_out = &cl->next;
 
-    if (ctx->zstream.avail_out >= 8) {
-        trailer = (struct gztrailer *) ctx->out_buf->last;
-        ctx->out_buf->last += 8;
-        ctx->out_buf->last_buf = 1;
-
-    } else {
-        b = ngx_create_temp_buf(r->pool, 8);
-        if (b == NULL) {
-            return NGX_ERROR;
-        }
-
-        b->last_buf = 1;
-
-        cl = ngx_alloc_chain_link(r->pool);
-        if (cl == NULL) {
-            return NGX_ERROR;
-        }
-
-        cl->buf = b;
-        cl->next = NULL;
-        *ctx->last_out = cl;
-        ctx->last_out = &cl->next;
-        trailer = (struct gztrailer *) b->pos;
-        b->last += 8;
-    }
-
-#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
-
-    trailer->crc32 = ctx->crc32;
-    trailer->zlen = ctx->zin;
-
-#else
-
-    trailer->crc32[0] = (u_char) (ctx->crc32 & 0xff);
-    trailer->crc32[1] = (u_char) ((ctx->crc32 >> 8) & 0xff);
-    trailer->crc32[2] = (u_char) ((ctx->crc32 >> 16) & 0xff);
-    trailer->crc32[3] = (u_char) ((ctx->crc32 >> 24) & 0xff);
-
-    trailer->zlen[0] = (u_char) (ctx->zin & 0xff);
-    trailer->zlen[1] = (u_char) ((ctx->zin >> 8) & 0xff);
-    trailer->zlen[2] = (u_char) ((ctx->zin >> 16) & 0xff);
-    trailer->zlen[3] = (u_char) ((ctx->zin >> 24) & 0xff);
-
-#endif
+    ctx->out_buf->last_buf = 1;
 
     ctx->zstream.avail_in = 0;
     ctx->zstream.avail_out = 0;
-- 
GitLab


From 676d1a0e947c8f39e2606997a3628ec6bdea177d Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Wed, 17 Jul 2019 17:00:57 +0300
Subject: [PATCH 22/33] Perl: removed unused variable, forgotten in
 975d7ab37b39.

---
 src/http/modules/perl/nginx.xs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs
index 309b7cbfb..caf7c084a 100644
--- a/src/http/modules/perl/nginx.xs
+++ b/src/http/modules/perl/nginx.xs
@@ -933,7 +933,6 @@ internal_redirect(r, uri)
     ngx_http_request_t   *r;
     ngx_http_perl_ctx_t  *ctx;
     SV                   *uri;
-    ngx_uint_t            i;
 
     ngx_http_perl_set_request(r, ctx);
 
-- 
GitLab


From 551640703a502a252b24c84d1b9e6541ee88de34 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 18 Jul 2019 18:27:44 +0300
Subject: [PATCH 23/33] Core: fixed segfault with too large bucket sizes
 (ticket #1806).

To save memory hash code uses u_short to store resulting bucket sizes,
so maximum bucket size is limited to 65536 minus ngx_cacheline_size (larger
values will be aligned to 65536 which will overflow u_short).  However,
there were no checks to enforce this, and using larger bucket sizes
resulted in overflows and segmentation faults.

Appropriate safety checks to enforce this added to ngx_hash_init().
---
 src/core/ngx_hash.c | 30 +++++++++++++++++++++++++-----
 1 file changed, 25 insertions(+), 5 deletions(-)

diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c
index 1944c7a21..d684e7057 100644
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -265,6 +265,14 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
         return NGX_ERROR;
     }
 
+    if (hinit->bucket_size > 65536 - ngx_cacheline_size) {
+        ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+                      "could not build %s, too large "
+                      "%s_bucket_size: %i",
+                      hinit->name, hinit->name, hinit->bucket_size);
+        return NGX_ERROR;
+    }
+
     for (n = 0; n < nelts; n++) {
         if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
         {
@@ -300,17 +308,19 @@ ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
             }
 
             key = names[n].key_hash % size;
-            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+            len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);
 
 #if 0
             ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
-                          "%ui: %ui %ui \"%V\"",
-                          size, key, test[key], &names[n].key);
+                          "%ui: %ui %uz \"%V\"",
+                          size, key, len, &names[n].key);
 #endif
 
-            if (test[key] > (u_short) bucket_size) {
+            if (len > bucket_size) {
                 goto next;
             }
+
+            test[key] = (u_short) len;
         }
 
         goto found;
@@ -341,7 +351,17 @@ found:
         }
 
         key = names[n].key_hash % size;
-        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+        len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);
+
+        if (len > 65536 - ngx_cacheline_size) {
+            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+                          "could not build %s, you should "
+                          "increase %s_max_size: %i",
+                          hinit->name, hinit->name, hinit->max_size);
+            return NGX_ERROR;
+        }
+
+        test[key] = (u_short) len;
     }
 
     len = 0;
-- 
GitLab


From 36dfa020f256dfc5beed3366be099d99543ad5b2 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 18 Jul 2019 18:27:50 +0300
Subject: [PATCH 24/33] HTTP/2: return error on output on closed stream.

Without this, an (incorrect) output on a closed stream could result in
a socket leak.
---
 src/http/v2/ngx_http_v2_filter_module.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/http/v2/ngx_http_v2_filter_module.c b/src/http/v2/ngx_http_v2_filter_module.c
index 853faefd3..81e6c513a 100644
--- a/src/http/v2/ngx_http_v2_filter_module.c
+++ b/src/http/v2/ngx_http_v2_filter_module.c
@@ -1444,6 +1444,12 @@ ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)
 
     if (in == NULL || stream->out_closed) {
 
+        if (size) {
+            ngx_log_error(NGX_LOG_ERR, fc->log, 0,
+                          "output on closed stream");
+            return NGX_CHAIN_ERROR;
+        }
+
         if (stream->queued) {
             fc->write->active = 1;
             fc->write->ready = 0;
-- 
GitLab


From 20c8c4fe35d290abe298cea9a4f1756fcfec19f4 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 18 Jul 2019 18:27:52 +0300
Subject: [PATCH 25/33] Upstream: fixed EOF handling in unbuffered and upgraded
 modes.

With level-triggered event methods it is important to specify
the NGX_CLOSE_EVENT flag to ngx_handle_read_event(), otherwise
the event won't be removed, resulting in CPU hog.

Reported by Patrick Wollgast.
---
 src/http/ngx_http_upstream.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index ffb09a91f..89e1319f9 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -3334,6 +3334,7 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
     size_t                     size;
     ssize_t                    n;
     ngx_buf_t                 *b;
+    ngx_uint_t                 flags;
     ngx_connection_t          *c, *downstream, *upstream, *dst, *src;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
@@ -3472,7 +3473,14 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
         ngx_del_timer(upstream->write);
     }
 
-    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+    if (upstream->read->eof || upstream->read->error) {
+        flags = NGX_CLOSE_EVENT;
+
+    } else {
+        flags = 0;
+    }
+
+    if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
         return;
     }
@@ -3491,7 +3499,14 @@ ngx_http_upstream_process_upgraded(ngx_http_request_t *r,
         return;
     }
 
-    if (ngx_handle_read_event(downstream->read, 0) != NGX_OK) {
+    if (downstream->read->eof || downstream->read->error) {
+        flags = NGX_CLOSE_EVENT;
+
+    } else {
+        flags = 0;
+    }
+
+    if (ngx_handle_read_event(downstream->read, flags) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
         return;
     }
@@ -3563,6 +3578,7 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
     ssize_t                    n;
     ngx_buf_t                 *b;
     ngx_int_t                  rc;
+    ngx_uint_t                 flags;
     ngx_connection_t          *downstream, *upstream;
     ngx_http_upstream_t       *u;
     ngx_http_core_loc_conf_t  *clcf;
@@ -3666,7 +3682,14 @@ ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,
         ngx_del_timer(downstream->write);
     }
 
-    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {
+    if (upstream->read->eof || upstream->read->error) {
+        flags = NGX_CLOSE_EVENT;
+
+    } else {
+        flags = 0;
+    }
+
+    if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {
         ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
         return;
     }
-- 
GitLab


From ad42d70fed67c1e7098055fb25721ab904db2389 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 18 Jul 2019 18:27:53 +0300
Subject: [PATCH 26/33] SSI: avoid potential buffer overflow.

When "-" follows a parameter of maximum length, a single byte buffer
overflow happens, since the error branch does not check parameter length.
Fix is to avoid saving "-" to the parameter key, and instead use an error
message with "-" explicitly written.  The message is mostly identical to
one used in similar cases in the preequal state.

Reported by Patrick Wollgast.
---
 src/http/modules/ngx_http_ssi_filter_module.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
index d608df9df..6737965d1 100644
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -1254,9 +1254,9 @@ ngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)
             case '-':
                 state = ssi_error_end0_state;
 
-                ctx->param->key.data[ctx->param->key.len++] = ch;
                 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                              "invalid \"%V\" parameter in \"%V\" SSI command",
+                              "unexpected \"-\" symbol after \"%V\" "
+                              "parameter in \"%V\" SSI command",
                               &ctx->param->key, &ctx->command);
                 break;
 
-- 
GitLab


From 2187586207e1465d289ae64cedc829719a048a39 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 18 Jul 2019 18:27:54 +0300
Subject: [PATCH 27/33] Xslt: fixed potential buffer overflow with null
 character.

Due to shortcomings of the ccv->zero flag implementation in complex value
interface, length of the resulting string from ngx_http_complex_value()
might either not include terminating null character or include it,
so the only safe way to work with the result is to use it as a
null-terminated string.

Reported by Patrick Wollgast.
---
 src/http/modules/ngx_http_xslt_filter_module.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/http/modules/ngx_http_xslt_filter_module.c b/src/http/modules/ngx_http_xslt_filter_module.c
index ea7ce2a5c..b2f107dc0 100644
--- a/src/http/modules/ngx_http_xslt_filter_module.c
+++ b/src/http/modules/ngx_http_xslt_filter_module.c
@@ -628,7 +628,7 @@ static ngx_int_t
 ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
     ngx_array_t *params, ngx_uint_t final)
 {
-    u_char                 *p, *last, *value, *dst, *src, **s;
+    u_char                 *p, *value, *dst, *src, **s;
     size_t                  len;
     ngx_uint_t              i;
     ngx_str_t               string;
@@ -698,8 +698,6 @@ ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
             ngx_memcpy(p, string.data, string.len + 1);
         }
 
-        last = p + string.len;
-
         while (p && *p) {
 
             value = p;
@@ -729,7 +727,7 @@ ngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,
                 *p++ = '\0';
 
             } else {
-                len = last - value;
+                len = ngx_strlen(value);
             }
 
             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-- 
GitLab


From c3fd5f7e76343c899747ec58ae703540e7e9e69a Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Fri, 19 Jul 2019 17:50:00 +0300
Subject: [PATCH 28/33] Core: fixed memory leak on error, missed in
 c3f60d618c17.

Found by Coverity (CID 1451664).
---
 src/core/ngx_hash.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/core/ngx_hash.c b/src/core/ngx_hash.c
index d684e7057..d9c157c1d 100644
--- a/src/core/ngx_hash.c
+++ b/src/core/ngx_hash.c
@@ -358,6 +358,7 @@ found:
                           "could not build %s, you should "
                           "increase %s_max_size: %i",
                           hinit->name, hinit->name, hinit->max_size);
+            ngx_free(test);
             return NGX_ERROR;
         }
 
-- 
GitLab


From 3eb7ef4e203809ebab49e0ae488890b6a901e014 Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 23 Jul 2019 15:01:47 +0300
Subject: [PATCH 29/33] nginx-1.17.2-RELEASE

---
 docs/xml/nginx/changes.xml | 89 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/docs/xml/nginx/changes.xml b/docs/xml/nginx/changes.xml
index 60221ab55..416de1f6c 100644
--- a/docs/xml/nginx/changes.xml
+++ b/docs/xml/nginx/changes.xml
@@ -5,6 +5,95 @@
 <change_log title="nginx">
 
 
+<changes ver="1.17.2" date="2019-07-23">
+
+<change type="change">
+<para lang="ru">
+Š¼ŠøŠ½ŠøŠ¼Š°Š»ŃŒŠ½Š°Ń¸ ŠæŠ¾Š´Š´ŠµŃ€Š¶ŠøŠ²Š°ŠµŠ¼Š°Ń¸ Š²ŠµŃ€ŃŠøѸ zlib&mdash;1.2.0.4.<br/>
+Š�ŠæŠ°ŃŠøŠ±Š¾ Š˜Š»ŃŒŠµ Š›ŠµŠ¾ŃˆŠŗŠµŠ²Šøчу.
+</para>
+<para lang="en">
+minimum supported zlib version is 1.2.0.4.<br/>
+Thanks to Ilya Leoshkevich.
+</para>
+</change>
+
+<change type="change">
+<para lang="ru">
+Š¼ŠµŃ‚Š¾Š´ $r->internal_redirect() Š²ŃŃ‚Ń€Š¾ŠµŠ½Š½Š¾Š³Š¾ ŠæŠµŃ€Š»Š°
+Ń‚ŠµŠæŠµŃ€ŃŒ Š¾Š¶ŠøŠ´Š°ŠµŃ‚ Š·Š°ŠŗŠ¾Š´ŠøŃ€Š¾Š²Š°Š½Š½Ń‹Š¹ URI.
+</para>
+<para lang="en">
+the $r->internal_redirect() embedded perl method
+now expects escaped URIs.
+</para>
+</change>
+
+<change type="feature">
+<para lang="ru">
+Ń‚ŠµŠæŠµŃ€ŃŒ с ŠæŠ¾Š¼Š¾Ń‰ŃŒŃˇ Š¼ŠµŃ‚Š¾Š´Š° $r->internal_redirect() Š²ŃŃ‚Ń€Š¾ŠµŠ½Š½Š¾Š³Š¾ ŠæŠµŃ€Š»Š°
+Š¼Š¾Š¶Š½Š¾ ŠæŠµŃ€ŠµŠ¹Ń‚Šø Š² ŠøŠ¼ŠµŠ½Š¾Š²Š°Š½Š½Ń‹Š¹ location.
+</para>
+<para lang="en">
+it is now possible to switch to a named location
+using the $r->internal_redirect() embedded perl method.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š¾Š±Ń€Š°Š±Š¾Ń‚ŠŗŠµ Š¾ŃˆŠøŠ±Š¾Šŗ Š²Š¾ Š²ŃŃ‚Ń€Š¾ŠµŠ½Š½Š¾Š¼ ŠæŠµŃ€Š»Šµ.
+</para>
+<para lang="en">
+in error handling in embedded perl.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š½Š° стŠ°Ń€Ń‚Šµ ŠøŠ»Šø Š²Š¾ Š²Ń€ŠµŠ¼Ń¸ ŠæŠµŃ€ŠµŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø Š¼Š¾Š³ ŠæŃ€Š¾ŠøŠ·Š¾Š¹Ń‚Šø segmentation fault,
+ŠµŃŠ»Šø Š² ŠŗŠ¾Š½Ń„ŠøŠ³ŃƒŃ€Š°Ń†ŠøŠø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š»Š¾ŃŃŒ Š·Š½Š°Ń‡ŠµŠ½ŠøŠµ hash bucket size Š±Š¾Š»ŃŒŃˆŠµ 64 ŠŗŠøŠ»Š¾Š±Š°Š¹Ń‚.
+</para>
+<para lang="en">
+a segmentation fault might occur on start or during reconfiguration
+if hash bucket size larger than 64 kilobytes was used in the configuration.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+ŠæŃ€Šø ŠøсŠæŠ¾Š»ŃŒŠ·Š¾Š²Š°Š½ŠøŠø Š¼ŠµŃ‚Š¾Š´Š¾Š² Š¾Š±Ń€Š°Š±Š¾Ń‚ŠŗŠø сŠ¾ŠµŠ´ŠøŠ½ŠµŠ½ŠøŠ¹ select, poll Šø /dev/poll
+nginx Š¼Š¾Š³ Š½Š°Š³Ń€ŃƒŠ¶Š°Ń‚ь ŠæŃ€Š¾Ń†ŠµŃŃŠ¾Ń€ Š²Š¾ Š²Ń€ŠµŠ¼Ń¸ Š½ŠµŠ±ŃƒŃ„ŠµŃ€ŠøŠ·Š¾Š²Š°Š½Š½Š¾Š³Š¾ ŠæŃ€Š¾ŠŗсŠøŃ€Š¾Š²Š°Š½ŠøѸ
+Šø ŠæŃ€Šø ŠæŃ€Š¾ŠŗсŠøŃ€Š¾Š²Š°Š½ŠøŠø WebSocket-сŠ¾ŠµŠ´ŠøŠ½ŠµŠ½ŠøŠ¹.
+</para>
+<para lang="en">
+nginx might hog CPU during unbuffered proxying
+and when proxying WebSocket connections
+if the select, poll, or /dev/poll methods were used.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š¼Š¾Š´ŃƒŠ»Šµ ngx_http_xslt_filter_module.
+</para>
+<para lang="en">
+in the ngx_http_xslt_filter_module.
+</para>
+</change>
+
+<change type="bugfix">
+<para lang="ru">
+Š² Š¼Š¾Š´ŃƒŠ»Šµ ngx_http_ssi_filter_module.
+</para>
+<para lang="en">
+in the ngx_http_ssi_filter_module.
+</para>
+</change>
+
+</changes>
+
+
 <changes ver="1.17.1" date="2019-06-25">
 
 <change type="feature">
-- 
GitLab


From e957ae888a986290dc789d71d31299fe5463e2fb Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Tue, 23 Jul 2019 15:01:47 +0300
Subject: [PATCH 30/33] release-1.17.2 tag

---
 .hgtags | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.hgtags b/.hgtags
index 10adccb73..7f2d14e33 100644
--- a/.hgtags
+++ b/.hgtags
@@ -440,3 +440,4 @@ d2fd76709909767fc727a5b4affcf1dc9ca488a7 release-1.15.9
 0130ca3d58437b3c7c707cdddd813d530c68da9a release-1.15.12
 054c1c46395caff79bb4caf16f40b331f71bb6dd release-1.17.0
 7816bd7dabf6ee86c53c073b90a7143161546e06 release-1.17.1
+2fc9f853a6b7cd29dc84e0af2ed3cf78e0da6ca8 release-1.17.2
-- 
GitLab


From 6179b98ed537e2ad39eff11ee1689bf4700e59af Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Wed, 31 Jul 2019 17:28:41 +0300
Subject: [PATCH 31/33] Version bump.

---
 src/core/nginx.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/nginx.h b/src/core/nginx.h
index 25c5a729f..40dbe4e05 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -9,8 +9,8 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define nginx_version      1017002
-#define NGINX_VERSION      "1.17.2"
+#define nginx_version      1017003
+#define NGINX_VERSION      "1.17.3"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #ifdef NGX_BUILD
-- 
GitLab


From 39c40428f93db246a9a27e7a109413fae46e195d Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Wed, 31 Jul 2019 17:29:00 +0300
Subject: [PATCH 32/33] Gzip: fixed "zero size buf" alerts after ac5a741d39cf.

After ac5a741d39cf it is now possible that after zstream.avail_out
reaches 0 and we allocate additional buffer, there will be no more data
to put into this buffer, triggering "zero size buf" alert.  Fix is to
reset b->temporary flag in this case.

Additionally, an optimization added to avoid allocating additional buffer
in this case, by checking if last deflate() call returned Z_STREAM_END.
Note that checking for Z_STREAM_END by itself is not enough to fix alerts,
as deflate() can return Z_STREAM_END without producing any output if the
buffer is smaller than gzip trailer.

Reported by Witold Filipczyk,
http://mailman.nginx.org/pipermail/nginx-devel/2019-July/012469.html.
---
 src/http/modules/ngx_http_gzip_filter_module.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c
index 48f3dd7c7..c75169c53 100644
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -778,7 +778,7 @@ ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)
 
     ctx->out_buf->last = ctx->zstream.next_out;
 
-    if (ctx->zstream.avail_out == 0) {
+    if (ctx->zstream.avail_out == 0 && rc != Z_STREAM_END) {
 
         /* zlib wants to output some more gzipped data */
 
@@ -868,6 +868,7 @@ ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
     ngx_http_gzip_ctx_t *ctx)
 {
     int           rc;
+    ngx_buf_t    *b;
     ngx_chain_t  *cl;
 
     ctx->zin = ctx->zstream.total_in;
@@ -888,13 +889,19 @@ ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,
         return NGX_ERROR;
     }
 
-    cl->buf = ctx->out_buf;
+    b = ctx->out_buf;
+
+    if (ngx_buf_size(b) == 0) {
+        b->temporary = 0;
+    }
+
+    b->last_buf = 1;
+
+    cl->buf = b;
     cl->next = NULL;
     *ctx->last_out = cl;
     ctx->last_out = &cl->next;
 
-    ctx->out_buf->last_buf = 1;
-
     ctx->zstream.avail_in = 0;
     ctx->zstream.avail_out = 0;
 
-- 
GitLab


From abe660636c93315b4acb8531b83aec8d309d2eca Mon Sep 17 00:00:00 2001
From: Maxim Dounin <mdounin@mdounin.ru>
Date: Thu, 1 Aug 2019 13:50:07 +0300
Subject: [PATCH 33/33] Mail: fixed duplicate resolving.

When using SMTP with SSL and resolver, read events might be enabled
during address resolving, leading to duplicate ngx_mail_ssl_handshake_handler()
calls if something arrives from the client, and duplicate session
initialization - including starting another resolving.  This can lead
to a segmentation fault if the session is closed after first resolving
finished.  Fix is to block read events while resolving.

Reported by Robert Norris,
http://mailman.nginx.org/pipermail/nginx/2019-July/058204.html.
---
 src/mail/ngx_mail_smtp_handler.c | 43 ++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/src/mail/ngx_mail_smtp_handler.c b/src/mail/ngx_mail_smtp_handler.c
index 939fb1a51..f1017e0d8 100644
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -15,6 +15,7 @@
 static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);
 static void ngx_mail_smtp_resolve_name(ngx_event_t *rev);
 static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);
+static void ngx_mail_smtp_block_reading(ngx_event_t *rev);
 static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);
 static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);
 static ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,
@@ -88,6 +89,9 @@ ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)
     ctx->data = s;
     ctx->timeout = cscf->resolver_timeout;
 
+    s->resolver_ctx = ctx;
+    c->read->handler = ngx_mail_smtp_block_reading;
+
     if (ngx_resolve_addr(ctx) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
@@ -169,6 +173,9 @@ ngx_mail_smtp_resolve_name(ngx_event_t *rev)
     ctx->data = s;
     ctx->timeout = cscf->resolver_timeout;
 
+    s->resolver_ctx = ctx;
+    c->read->handler = ngx_mail_smtp_block_reading;
+
     if (ngx_resolve_name(ctx) != NGX_OK) {
         ngx_mail_close_connection(c);
     }
@@ -238,6 +245,38 @@ found:
 }
 
 
+static void
+ngx_mail_smtp_block_reading(ngx_event_t *rev)
+{
+    ngx_connection_t    *c;
+    ngx_mail_session_t  *s;
+    ngx_resolver_ctx_t  *ctx;
+
+    c = rev->data;
+    s = c->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "smtp reading blocked");
+
+    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
+
+        if (s->resolver_ctx) {
+            ctx = s->resolver_ctx;
+
+            if (ctx->handler == ngx_mail_smtp_resolve_addr_handler) {
+                ngx_resolve_addr_done(ctx);
+
+            } else if (ctx->handler == ngx_mail_smtp_resolve_name_handler) {
+                ngx_resolve_name_done(ctx);
+            }
+
+            s->resolver_ctx = NULL;
+        }
+
+        ngx_mail_close_connection(c);
+    }
+}
+
+
 static void
 ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
 {
@@ -258,6 +297,10 @@ ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)
         ngx_mail_close_connection(c);
     }
 
+    if (c->read->ready) {
+        ngx_post_event(c->read, &ngx_posted_events);
+    }
+
     if (sscf->greeting_delay) {
          c->read->handler = ngx_mail_smtp_invalid_pipelining;
          return;
-- 
GitLab