From c99b19ae364474dd72c64674eb094ad54be3f905 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 4 Jun 2026 18:58:27 -0500 Subject: [PATCH] Fix Perl async callback refcounts --- src/http/modules/perl/nginx.xs | 26 +++++- src/http/modules/perl/ngx_http_perl_module.c | 83 ++++++++++++++++---- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/src/http/modules/perl/nginx.xs b/src/http/modules/perl/nginx.xs index fd59e29ea..6ce612287 100644 --- a/src/http/modules/perl/nginx.xs +++ b/src/http/modules/perl/nginx.xs @@ -396,6 +396,7 @@ has_request_body(r, next) ngx_http_request_t *r; ngx_http_perl_ctx_t *ctx; ngx_int_t rc; + SV *next; ngx_http_perl_set_request(r, ctx); @@ -407,11 +408,18 @@ has_request_body(r, next) croak("has_request_body(): another handler active"); } + next = ST(1); + + if (!SvROK(next) || SvTYPE(SvRV(next)) != SVt_PVCV) { + croak("has_request_body(): next is not a CODE reference"); + } + if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) { XSRETURN_UNDEF; } - ctx->next = SvRV(ST(1)); + ctx->next = SvRV(next); + SvREFCNT_inc(ctx->next); r->request_body_in_single_buf = 1; r->request_body_in_persistent_file = 1; @@ -423,9 +431,12 @@ has_request_body(r, next) rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request); - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { ctx->error = 1; - ctx->status = rc; + ctx->status = (rc == NGX_ERROR) ? NGX_HTTP_INTERNAL_SERVER_ERROR : rc; + if (ctx->next) { + SvREFCNT_dec(ctx->next); + } ctx->next = NULL; croak("ngx_http_read_client_request_body() failed"); } @@ -1130,6 +1141,7 @@ sleep(r, sleep, next) ngx_http_request_t *r; ngx_http_perl_ctx_t *ctx; ngx_msec_t sleep; + SV *next; ngx_http_perl_set_request(r, ctx); @@ -1142,11 +1154,17 @@ sleep(r, sleep, next) } sleep = (ngx_msec_t) SvIV(ST(1)); + next = ST(2); + + if (!SvROK(next) || SvTYPE(SvRV(next)) != SVt_PVCV) { + croak("sleep(): next is not a CODE reference"); + } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "perl sleep: %M", sleep); - ctx->next = SvRV(ST(2)); + ctx->next = SvRV(next); + SvREFCNT_inc(ctx->next); r->connection->write->delayed = 1; ngx_add_timer(r->connection->write, sleep); diff --git a/src/http/modules/perl/ngx_http_perl_module.c b/src/http/modules/perl/ngx_http_perl_module.c index f3fc62947..a6c95b3ff 100644 --- a/src/http/modules/perl/ngx_http_perl_module.c +++ b/src/http/modules/perl/ngx_http_perl_module.c @@ -46,6 +46,8 @@ static ngx_int_t 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); static void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv); +static ngx_http_perl_ctx_t *ngx_http_perl_create_ctx(ngx_http_request_t *r); +static void ngx_http_perl_cleanup_ctx(void *data); static ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf); static void *ngx_http_perl_create_main_conf(ngx_conf_t *cf); @@ -184,7 +186,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_uint_t flags, async; ngx_http_perl_ctx_t *ctx; ngx_http_perl_loc_conf_t *plcf; ngx_http_perl_main_conf_t *pmcf; @@ -194,15 +196,11 @@ ngx_http_perl_handle_request(ngx_http_request_t *r) ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); if (ctx == NULL) { - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t)); + ctx = ngx_http_perl_create_ctx(r); if (ctx == NULL) { ngx_http_finalize_request(r, NGX_ERROR); return; } - - 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); @@ -217,16 +215,22 @@ ngx_http_perl_handle_request(ngx_http_request_t *r) plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module); sub = plcf->sub; handler = &plcf->handler; + async = 0; } else { sub = ctx->next; handler = &ngx_null_name; ctx->next = NULL; + async = 1; } rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sub, NULL, handler, NULL); + if (async) { + SvREFCNT_dec(sub); + } + } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, @@ -326,14 +330,10 @@ ngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); if (ctx == NULL) { - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t)); + ctx = ngx_http_perl_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } - - ngx_http_set_ctx(r, ctx, ngx_http_perl_module); - - ctx->request = r; } saved = ctx->variable; @@ -395,14 +395,10 @@ ngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx, ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module); if (ctx == NULL) { - ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t)); + ctx = ngx_http_perl_create_ctx(r); if (ctx == NULL) { return NGX_ERROR; } - - 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); @@ -842,6 +838,61 @@ ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv) } +static ngx_http_perl_ctx_t * +ngx_http_perl_create_ctx(ngx_http_request_t *r) +{ + ngx_pool_cleanup_t *cln; + ngx_http_perl_ctx_t *ctx; + + ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t)); + if (ctx == NULL) { + return NULL; + } + + cln = ngx_pool_cleanup_add(r->pool, 0); + if (cln == NULL) { + return NULL; + } + + cln->handler = ngx_http_perl_cleanup_ctx; + cln->data = ctx; + + ngx_http_set_ctx(r, ctx, ngx_http_perl_module); + + ctx->request = r; + + return ctx; +} + + +static void +ngx_http_perl_cleanup_ctx(void *data) +{ + ngx_http_perl_ctx_t *ctx = data; + SV *next; + ngx_http_perl_main_conf_t *pmcf; + + if (ctx->next == NULL) { + return; + } + + pmcf = ngx_http_get_module_main_conf(ctx->request, ngx_http_perl_module); + + { + + dTHXa(pmcf->perl); + PERL_SET_CONTEXT(pmcf->perl); + PERL_SET_INTERP(pmcf->perl); + + next = ctx->next; + ctx->next = NULL; + + SvREFCNT_dec(next); + + } +} + + static void * ngx_http_perl_create_main_conf(ngx_conf_t *cf) {