Opened 4 years ago

Closed 3 years ago

#2058 closed defect (fixed)

nginx returns code 500 on zero-size request body when filter module returns 40x

Reported by: meelisroos@… Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.19.x
Keywords: filter module 500 Cc: meelisroos@…
uname -a: Linux test-nginx 3.10.0-957.10.1.el7.x86_64 #1 SMP Mon Mar 18 15:06:45 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.19.3
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-module=/home/builder/rpmbuild/BUILD/nginx-1.19.3/ngx_http_catch_body_filter_module

Description

When nginx HTTP filter module processes the request body and filter returns 40x (400 and 403 tested), nginx returns this response code to client if request body has non-zero size and fail with error 500 when request body size is 0 (and the filter module returns answer 40x).

I am debugging my own request body filter module for nginx proxy. I found a problem so I reduced it to minimal reproducer code fragment.

I took http://mdounin.ru/hg/ngx_http_catch_body_filter_module as a base and applied byte counter patch to it, to return 40x on zero or 1 byte body size. For 1-byte body, the 40x response is returned to the client, and for 0-byte body, 500 is returned.

Found on nginx 1.18.0 and tested on 1.19.3.

Patch for my reproducer. In configuration, I have the module turned on for /test:

location = /test {

set_real_ip_from 192.168.0.1;
real_ip_header X-Forwarded-For;

# Check content.
catch_body on;
proxy_request_buffering on;

# Proxied server.
proxy_pass http://localhost:808/blobserver;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;

}

The test:
curl -s -S -H 'Content-Type: application/octet-stream' --data-binary http://localhost/test

debug log results in this:
2020/10/08 22:07:49 [debug] 23777#23777: *1 generic phase: 13
2020/10/08 22:07:49 [debug] 23777#23777: *1 http request body content length filter
2020/10/08 22:07:49 [debug] 23777#23777: *1 catch request body filter
2020/10/08 22:07:49 [debug] 23777#23777: *1 catch body: wrong length
2020/10/08 22:07:49 [debug] 23777#23777: *1 http finalize request: 500, "/test?" a:1, c:1
2020/10/08 22:07:49 [debug] 23777#23777: *1 http special response: 500, "/test?"
2020/10/08 22:07:49 [debug] 23777#23777: *1 HTTP/1.1 500 Internal Server Error

Attachments (1)

ngx-catch-body.patch (1008 bytes ) - added by meelisroos@… 4 years ago.
reproducer as patch to ngx_http_catch_body_filter_module

Download all attachments as: .zip

Change History (4)

by meelisroos@…, 4 years ago

Attachment: ngx-catch-body.patch added

reproducer as patch to ngx_http_catch_body_filter_module

comment:1 by Maxim Dounin, 4 years ago

Thanks for reporting this. Looks like a leftover from before request body filters were introduced: previously, the only possible return code on the initial ngx_http_request_body_filter() call without any buffers was 500. Please try the following patch:

# HG changeset patch
# User Maxim Dounin <mdounin@mdounin.ru>
# Date 1602265457 -10800
#      Fri Oct 09 20:44:17 2020 +0300
# Node ID f4f9d2d02b4c840ed0c34628818b444c7b21cd83
# Parent  559d19037984d062781da0419c8b82aa3c1b415d
Request body: removed error assumption (ticket #2058).

Before introduction of request body filter in 42d9beeb22db, the only
possible return code from the ngx_http_request_body_filter() call
without actual buffers was NGX_HTTP_INTERNAL_SERVER_ERROR, and
the code in ngx_http_read_client_request_body() hardcoded the only
possible error to simplify the code of initial call to set rb->buf.

This is non longer true after introduction of request body filters though,
as a request body filter might need to return other errors, such as 403.
Fix is to preserve the error code actually returned by the call
instead of assuming 500.

diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -137,8 +137,9 @@ ngx_http_read_client_request_body(ngx_ht
     } else {
         /* set rb->rest */
 
-        if (ngx_http_request_body_filter(r, NULL) != NGX_OK) {
-            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
+        rc = ngx_http_request_body_filter(r, NULL);
+
+        if (rc != NGX_OK) {
             goto done;
         }
     }

comment:2 by Maxim Dounin <mdounin@…>, 3 years ago

In 7740:967cfa6e2ff8/nginx:

Request body: removed error assumption (ticket #2058).

Before introduction of request body filter in 42d9beeb22db, the only
possible return code from the ngx_http_request_body_filter() call
without actual buffers was NGX_HTTP_INTERNAL_SERVER_ERROR, and
the code in ngx_http_read_client_request_body() hardcoded the only
possible error to simplify the code of initial call to set rb->rest.

This is no longer true after introduction of request body filters though,
as a request body filter might need to return other errors, such as 403.
Fix is to preserve the error code actually returned by the call
instead of assuming 500.

comment:3 by Maxim Dounin, 3 years ago

Resolution: fixed
Status: newclosed

Fix committed, thanks for reporting this.

Note: See TracTickets for help on using tickets.