comparison src/http/modules/ngx_http_limit_req_module.c @ 7726:559d19037984

Limit req: unlocking of nodes on complex value errors. Previously, if there were multiple limits configured, errors in ngx_http_complex_value() during processing of a non-first limit resulted in reference count leak in shared memory nodes of already processed limits. Fix is to explicity unlock relevant nodes, much like we do when rejecting requests.
author Maxim Dounin <mdounin@mdounin.ru>
date Thu, 08 Oct 2020 17:44:34 +0300
parents 776d1bebdca2
children 1ebd78df4ce7
comparison
equal deleted inserted replaced
7725:d63c5373b5ba 7726:559d19037984
67 static void ngx_http_limit_req_delay(ngx_http_request_t *r); 67 static void ngx_http_limit_req_delay(ngx_http_request_t *r);
68 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, 68 static ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,
69 ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account); 69 ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);
70 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, 70 static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,
71 ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit); 71 ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);
72 static void ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits,
73 ngx_uint_t n);
72 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, 74 static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,
73 ngx_uint_t n); 75 ngx_uint_t n);
74 76
75 static ngx_int_t ngx_http_limit_req_status_variable(ngx_http_request_t *r, 77 static ngx_int_t ngx_http_limit_req_status_variable(ngx_http_request_t *r,
76 ngx_http_variable_value_t *v, uintptr_t data); 78 ngx_http_variable_value_t *v, uintptr_t data);
221 limit = &limits[n]; 223 limit = &limits[n];
222 224
223 ctx = limit->shm_zone->data; 225 ctx = limit->shm_zone->data;
224 226
225 if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { 227 if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
228 ngx_http_limit_req_unlock(limits, n);
226 return NGX_HTTP_INTERNAL_SERVER_ERROR; 229 return NGX_HTTP_INTERNAL_SERVER_ERROR;
227 } 230 }
228 231
229 if (key.len == 0) { 232 if (key.len == 0) {
230 continue; 233 continue;
268 lrcf->dry_run ? ", dry run" : "", 271 lrcf->dry_run ? ", dry run" : "",
269 excess / 1000, excess % 1000, 272 excess / 1000, excess % 1000,
270 &limit->shm_zone->shm.name); 273 &limit->shm_zone->shm.name);
271 } 274 }
272 275
273 while (n--) { 276 ngx_http_limit_req_unlock(limits, n);
274 ctx = limits[n].shm_zone->data;
275
276 if (ctx->node == NULL) {
277 continue;
278 }
279
280 ngx_shmtx_lock(&ctx->shpool->mutex);
281
282 ctx->node->count--;
283
284 ngx_shmtx_unlock(&ctx->shpool->mutex);
285
286 ctx->node = NULL;
287 }
288 277
289 if (lrcf->dry_run) { 278 if (lrcf->dry_run) {
290 r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN; 279 r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN;
291 return NGX_DECLINED; 280 return NGX_DECLINED;
292 } 281 }
607 *limit = &limits[n]; 596 *limit = &limits[n];
608 } 597 }
609 } 598 }
610 599
611 return max_delay; 600 return max_delay;
601 }
602
603
604 static void
605 ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits, ngx_uint_t n)
606 {
607 ngx_http_limit_req_ctx_t *ctx;
608
609 while (n--) {
610 ctx = limits[n].shm_zone->data;
611
612 if (ctx->node == NULL) {
613 continue;
614 }
615
616 ngx_shmtx_lock(&ctx->shpool->mutex);
617
618 ctx->node->count--;
619
620 ngx_shmtx_unlock(&ctx->shpool->mutex);
621
622 ctx->node = NULL;
623 }
612 } 624 }
613 625
614 626
615 static void 627 static void
616 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) 628 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)