Skip to content

Commit b405850

Browse files
author
Felipe Zimmerle
committed
nginx: copies the req body chain to be processed instead of move
Add a check for the definition MOVE_REQUEST_CHAIN_TO_MODSEC, whenever it is set the chain will be moved into the brigade. If it was not set the chain will be only copied. Moving was causing segfaults on the following regression tests: #15 - SecRequestBodyInMemoryLimit #16 - SecRequestBodyInMemoryLimit (greater) #19 - SecRequestBodyLimitAction ProcessPartial (multipart/greater - chunked) (from: regression/config/10-request-directives.t)
1 parent 5d8fa16 commit b405850

File tree

3 files changed

+113
-31
lines changed

3 files changed

+113
-31
lines changed

nginx/modsecurity/apr_bucket_nginx.c

+30
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,36 @@ ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) {
154154
}
155155

156156
ngx_int_t
157+
copy_chain_to_brigade(ngx_chain_t *chain_orig, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) {
158+
apr_bucket *e;
159+
160+
ngx_chain_t *chain = chain_orig;
161+
while (chain) {
162+
e = ngx_buf_to_apr_bucket(chain->buf, bb->p, bb->bucket_alloc);
163+
if (e == NULL) {
164+
return NGX_ERROR;
165+
}
166+
167+
APR_BRIGADE_INSERT_TAIL(bb, e);
168+
if (chain->buf->last_buf) {
169+
e = apr_bucket_eos_create(bb->bucket_alloc);
170+
APR_BRIGADE_INSERT_TAIL(bb, e);
171+
return NGX_OK;
172+
}
173+
chain = chain->next;
174+
}
175+
176+
if (last_buf) {
177+
e = apr_bucket_eos_create(bb->bucket_alloc);
178+
APR_BRIGADE_INSERT_TAIL(bb, e);
179+
return NGX_OK;
180+
}
181+
182+
return NGX_AGAIN;
183+
}
184+
185+
186+
ngx_int_t
157187
move_chain_to_brigade(ngx_chain_t *chain_orig, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf) {
158188
apr_bucket *e;
159189
ngx_chain_t *cl;

nginx/modsecurity/apr_bucket_nginx.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ apr_bucket * apr_bucket_nginx_make(apr_bucket *e, ngx_buf_t *buf,
1313

1414
ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool);
1515

16+
ngx_int_t copy_chain_to_brigade(ngx_chain_t *chain_orig,
17+
apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf);
18+
1619
ngx_int_t move_chain_to_brigade(ngx_chain_t *chain, apr_bucket_brigade *bb, ngx_pool_t *pool, ngx_int_t last_buf);
1720
ngx_int_t move_brigade_to_chain(apr_bucket_brigade *bb, ngx_chain_t **chain, ngx_pool_t *pool);
1821

nginx/modsecurity/ngx_http_modsecurity.c

+80-31
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
#include <apr_base64.h>
2020

21-
2221
/* Those are defined twice, lets keep it defined just once by `undef`
2322
* the first one.
2423
*/
@@ -31,6 +30,29 @@
3130
#define NOTE_NGINX_REQUEST_CTX "nginx-ctx"
3231
#define TXID_SIZE 25
3332

33+
/*
34+
* If MOVE_REQUEST_CHAIN_TO_MODSEC is set, the chain will be moved into the
35+
* ModSecurity brigade, after be processed by ModSecurity, it needs to be moved
36+
* to nginx chains again, in order to be processed by other modules. It seems
37+
* that this is not working well whenever the request body is delivered into
38+
* chunks. Resulting in segfaults.
39+
*
40+
* If MOVE_REQUEST_CHAIN_TO_MODSEC is _not_ set, a copy of the request body
41+
* will be delivered to ModSecurity and after processed it will be released,
42+
* not modifying the nginx chain.
43+
*
44+
* The malfunctioning can be observer while running the following regression
45+
* tests:
46+
* #15 - SecRequestBodyInMemoryLimit
47+
* #16 - SecRequestBodyInMemoryLimit (greater)
48+
* #19 - SecRequestBodyLimitAction ProcessPartial (multipart/greater - chunked)
49+
* (from: regression/config/10-request-directives.t)
50+
*/
51+
/*
52+
#define MOVE_REQUEST_CHAIN_TO_MODSEC
53+
*/
54+
55+
3456
#define tuxb
3557

3658
/*
@@ -492,8 +514,8 @@ ngx_http_modsecurity_save_headers_in_visitor(void *data, const char *key, const
492514
static ngx_inline ngx_int_t
493515
ngx_http_modsecurity_load_request_body(ngx_http_request_t *r)
494516
{
495-
ngx_http_modsecurity_ctx_t *ctx;
496-
517+
ngx_http_modsecurity_ctx_t *ctx;
518+
ngx_chain_t *chain;
497519

498520
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
499521
"ModSec: loading request body.");
@@ -502,33 +524,47 @@ ngx_http_modsecurity_load_request_body(ngx_http_request_t *r)
502524

503525
modsecSetBodyBrigade(ctx->req, ctx->brigade);
504526

505-
if (r->request_body == NULL || r->request_body->bufs == NULL) {
506-
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
507-
"ModSec: request was null.");
508-
509-
return move_chain_to_brigade(NULL, ctx->brigade, r->pool, 1);
527+
if (r->request_body == NULL) {
528+
chain = NULL;
510529
}
530+
else {
531+
chain = r->request_body->bufs;
532+
}
533+
511534

512-
if (move_chain_to_brigade(r->request_body->bufs, ctx->brigade, r->pool, 1) != NGX_OK) {
535+
#ifdef MOVE_REQUEST_CHAIN_TO_MODSEC
536+
if (move_chain_to_brigade(chain, ctx->brigade, r->pool, 1) != NGX_OK) {
513537
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
514538
"ModSec: failed to move chain to brigade.");
515539

516540
return NGX_ERROR;
517541
}
518542

519543
r->request_body = NULL;
544+
#else
545+
if (copy_chain_to_brigade(chain, ctx->brigade, r->pool, 1) != NGX_OK) {
546+
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
547+
"ModSec: failed to copy chain to brigade.");
548+
549+
return NGX_ERROR;
550+
}
551+
#endif
520552

521553
return NGX_OK;
522554
}
523555

524556
static ngx_inline ngx_int_t
525557
ngx_http_modsecurity_save_request_body(ngx_http_request_t *r)
526558
{
527-
ngx_http_modsecurity_ctx_t *ctx;
528-
apr_off_t content_length;
529-
ngx_buf_t *buf;
530-
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
559+
ngx_http_modsecurity_ctx_t *ctx;
560+
#ifdef MOVE_REQUEST_CHAIN_TO_MODSEC
561+
apr_off_t content_length;
562+
ngx_buf_t *buf;
563+
#endif
564+
565+
ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity);
531566

567+
#ifdef MOVE_REQUEST_CHAIN_TO_MODSEC
532568
apr_brigade_length(ctx->brigade, 0, &content_length);
533569

534570
buf = ngx_create_temp_buf(ctx->r->pool, (size_t) content_length);
@@ -550,10 +586,15 @@ ngx_http_modsecurity_save_request_body(ngx_http_request_t *r)
550586

551587
}
552588

553-
554589
r->headers_in.content_length_n = content_length;
555590

556-
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSec: Content length: %O, Content length n: %O", content_length, r->headers_in.content_length_n);
591+
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
592+
"ModSec: Content length: %O, Content length n: %O", content_length,
593+
r->headers_in.content_length_n);
594+
#else
595+
apr_brigade_cleanup(ctx->brigade);
596+
#endif
597+
557598
return NGX_OK;
558599
}
559600

@@ -787,7 +828,7 @@ ngx_http_modsecurity_status(ngx_http_request_t *r, int status)
787828
* @param r pointer to ngx_http_request_t.
788829
*
789830
*/
790-
static void
831+
ngx_int_t
791832
ngx_http_modsecurity_process_request(ngx_http_request_t *r)
792833
{
793834
ngx_int_t rc = 0;
@@ -802,23 +843,21 @@ ngx_http_modsecurity_process_request(ngx_http_request_t *r)
802843
if (ngx_http_modsecurity_load_request(r) != NGX_OK) {
803844
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
804845
"ModSec: failed while loading the request.");
805-
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
806-
goto terminate;
846+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
807847
}
808848

809849
if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK) {
810850
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
811851
"ModSec: failed while loading the headers.");
812-
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
813-
goto terminate;
852+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
814853
}
815854

816855
rc = modsecProcessRequestHeaders(ctx->req);
817856
rc = ngx_http_modsecurity_status(r, rc);
818857
if (rc != NGX_DECLINED) {
819858
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
820859
"ModSec: failed while processing the request headers");
821-
ngx_http_finalize_request(r, rc);
860+
return rc;
822861
}
823862

824863
/* Here we check if ModSecurity is enabled or disabled again.
@@ -841,12 +880,15 @@ ngx_http_modsecurity_process_request(ngx_http_request_t *r)
841880
}
842881

843882
if (load_request_body == 1) {
883+
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
884+
"ModSec: loading request body...");
885+
844886
rc = ngx_http_modsecurity_load_request_body(r);
845887
if (rc != NGX_OK)
846888
{
847889
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
848890
"ModSec: failed while loading the request body.");
849-
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
891+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
850892
}
851893

852894

@@ -862,24 +904,23 @@ ngx_http_modsecurity_process_request(ngx_http_request_t *r)
862904
"ModSec: finalizing the connection after process the " \
863905
"request body.");
864906

865-
ngx_http_finalize_request(r, rc);
907+
return rc;
866908
}
867909
if (load_request_body == 1) {
868910
if (ngx_http_modsecurity_save_request_body(r) != NGX_OK)
869911
{
870912
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
871913
"ModSec: failed while saving the request body");
872-
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
914+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
873915
}
874916
if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK)
875917
{
876918
ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
877919
"ModSec: failed while saving the headers in");
878-
ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
920+
return NGX_HTTP_INTERNAL_SERVER_ERROR;
879921
}
880922
}
881-
terminate:
882-
return;
923+
return NGX_OK;
883924
}
884925

885926

@@ -1102,14 +1143,22 @@ ngx_http_modsecurity_handler(ngx_http_request_t *r) {
11021143

11031144
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
11041145
"ModSec: request is ready to be processed.");
1105-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1106-
"ModSec: chuncked? %d", r->chunked);
1107-
ngx_http_modsecurity_process_request(r);
1146+
rc = ngx_http_modsecurity_process_request(r);
11081147
ctx->request_processed = 1;
1148+
1149+
if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
1150+
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1151+
"ModSec: returning a special response after process " \
1152+
"a request: %d", rc);
1153+
1154+
return rc;
1155+
}
1156+
1157+
11091158
}
11101159

11111160
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
1112-
"ModSec: returning NGX_OK. Count++ :P" );
1161+
"ModSec: returning NGX_DECLINED." );
11131162

11141163
return NGX_DECLINED;
11151164
}

0 commit comments

Comments
 (0)