diff --git a/CHANGES b/CHANGES index 8ae835854..531655f3c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,15 @@ Changes with FreeUnit 1.35.4 xx xxx 2026 + *) Bugfix: pass a non-zero ovector size to PCRE2 match-data create + (a 0 size is undefined per the PCRE2 docs) and reject malformed + port ranges of fewer than three bytes in HTTP routing. Also + documents the symmetric pattern/request URI decoding, the + intentionally case-insensitive host matcher, and that the + Linux capability module does not call capset(2) โ€” privilege + barriers in Unit are setuid + PR_SET_NO_NEW_PRIVS, not cap + drops. + *) Bugfix: fix router process CPU spin and connection hang under port scanning load; CLOSE-WAIT sockets are now cleaned up properly on client FIN, idle connection queue iteration fixed, systemd file diff --git a/src/nxt_capability.c b/src/nxt_capability.c index 9f36ab997..157c1eadb 100644 --- a/src/nxt_capability.c +++ b/src/nxt_capability.c @@ -3,6 +3,18 @@ * Copyright (C) NGINX, Inc. */ +/* + * NOTE: this module currently exposes capability *detection* + * (capget), used by nxt_isolation.c and the credential machinery to + * decide whether a non-root build can honor setuid/setgid in the + * "user"/"group" config keys. Capabilities are NOT dropped + * programmatically via capset(2): the privilege barrier for app + * processes is the existing setuid + PR_SET_NO_NEW_PRIVS dance in + * nxt_credential.c / nxt_process.c. Operators expecting an explicit + * capability-drop step should not assume one exists; isolation in + * Unit relies on uid/namespace/seccomp boundaries, not capset. + */ + #include #if (NXT_HAVE_LINUX_CAPABILITY) diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index bd0646f3b..601b1749a 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -493,6 +493,13 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } if (mtcf.host != NULL) { + /* + * Host matching is intentionally always case-insensitive + * (LOWCASE). Per RFC 9110 ยง4.2.3 the host component is + * case-insensitive, and there is no per-rule sensitivity + * flag โ€” an admin expecting case-sensitive host filtering + * will not get it from this rule. + */ rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1, NXT_HTTP_ROUTE_PATTERN_LOWCASE, NXT_HTTP_URI_ENCODING_NONE); @@ -1176,6 +1183,15 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, } +/* + * Configured pattern strings are decoded once here at compile time, + * while request URIs are decoded by the HTTP parser before they reach + * the matcher. Both paths funnel through the same nxt_decode_uri / + * nxt_decode_uri_plus helpers; %XX semantics are symmetric, so a + * "%2e%2e" in the configured pattern will compare against the same + * bytes a request "%2e%2e" decoded into. If either helper's behaviour + * changes, route patterns and request URIs MUST be updated in lock-step. + */ static nxt_int_t nxt_http_route_decode_str(nxt_str_t *str, nxt_http_uri_encoding_t encoding) { @@ -2147,7 +2163,15 @@ nxt_http_route_pattern(nxt_http_request_t *r, nxt_http_route_pattern_t *pattern, #if (NXT_HAVE_REGEX) if (pattern->regex) { if (r->regex_match == NULL) { - r->regex_match = nxt_regex_match_create(r->mem_pool, 0); + /* + * Reuse one match-data struct across every pattern compiled + * against this request, so size it for the minimum ovector + * (one offset pair โ€” the overall match). Captures are not + * consulted by the matcher. Passing 0 to PCRE2's + * pcre2_match_data_create() is undefined per the public + * docs; 1 is the documented minimum. + */ + r->regex_match = nxt_regex_match_create(r->mem_pool, 1); if (nxt_slow_path(r->regex_match == NULL)) { return NXT_ERROR; } diff --git a/src/nxt_http_route_addr.c b/src/nxt_http_route_addr.c index 5a0d7679d..5eee3bd16 100644 --- a/src/nxt_http_route_addr.c +++ b/src/nxt_http_route_addr.c @@ -261,6 +261,13 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, goto parse_port; } + + /* + * /32 is the single-host case; set EXACT explicitly so the + * state machine reflects intent rather than relying on the + * implicit assignment in the fallthrough path below. + */ + base->match_type = NXT_HTTP_ROUTE_ADDR_EXACT; } inet->start = nxt_inet_addr(addr.start, addr.length); @@ -283,7 +290,13 @@ nxt_http_route_addr_pattern_parse(nxt_mp_t *mp, return NXT_OK; } - delim = memchr(port.start, '-', port.length - 1); + /* + * Search the entire port string for '-' (the original "-1" bound + * silently ignored a trailing dash in inputs like "8-"). If the + * dash is at either end the resulting empty half makes + * nxt_int_parse() return < 0 below, producing PATTERN_PORT_ERROR. + */ + delim = memchr(port.start, '-', port.length); if (delim != NULL) { ret = nxt_int_parse(port.start, delim - port.start); if (nxt_slow_path(ret < 0 || ret > 65535)) {