|
| 1 | +Changes with FreeUnit 1.36.0 16 Jul 2026 |
| 2 | + |
| 3 | + *) Bugfix: the controller crashed in debug builds when a control-socket |
| 4 | + connection closed after the active-connection tracking change, and |
| 5 | + leaked the accepted file descriptor when a control connection failed |
| 6 | + during initialization or body allocation; connection tracking and |
| 7 | + teardown now route through the engine's tracking macros and the |
| 8 | + close path. |
| 9 | + |
| 10 | + *) Bugfix: ignore a malformed incoming "traceparent" header and restart |
| 11 | + the trace per W3C Trace Context instead of rejecting the request |
| 12 | + with HTTP 500; the malformed header is dropped instead of being |
| 13 | + forwarded alongside the restarted one, and context accepted from a |
| 14 | + valid duplicate is preserved. Values are now validated in full |
| 15 | + (segment lengths, lowercase hex, no all-zero ids, no "ff" version), |
| 16 | + so content-invalid values restart the trace instead of being |
| 17 | + re-emitted verbatim. An inbound "tracestate" is dropped together |
| 18 | + with the rejected context (kept when the traceparent is inherited). |
| 19 | + The injected "traceparent" fields are now zero-initialized; garbage |
| 20 | + flag bits could get the header randomly dropped from proxied |
| 21 | + requests and responses. |
| 22 | + |
| 23 | + *) Bugfix: reject empty values and embedded NUL bytes in configuration |
| 24 | + strings that are later used as NUL-terminated C strings ("user", |
| 25 | + "group", "working_directory", "stdout", "stderr", "executable", and |
| 26 | + the per-language application targets) and in pathname unix socket |
| 27 | + addresses, closing a silent-truncation target confusion. |
| 28 | + |
| 29 | + *) Bugfix: reject empty values and embedded NUL bytes in the remaining |
| 30 | + configuration strings used as NUL-terminated C strings outside |
| 31 | + application options: the "access_log" path (string and object |
| 32 | + forms), the TLS "certificate" and njs "js_module" store names, and |
| 33 | + the PHP "options.file" php.ini path. |
| 34 | + |
| 35 | + *) Bugfix: reject an embedded NUL byte in a templated static-serving |
| 36 | + path ("share" and "chroot") after it is resolved from |
| 37 | + request-controlled variables and before it reaches open()/openat2(), |
| 38 | + preventing a silent truncation that could serve a different file |
| 39 | + than the resolved path. |
| 40 | + |
| 41 | + *) Bugfix: mount rootfs automount destinations onto the openat2 |
| 42 | + validated descriptor via "/proc/self/fd" instead of re-resolving the |
| 43 | + path, closing a check-to-use window in which a destination component |
| 44 | + could be swapped for a symlink escaping the rootfs. |
| 45 | + |
| 46 | + *) Bugfix: treat duplicate upstream "Content-Length" headers in a |
| 47 | + proxied response as inconsistent — both headers are dropped and the |
| 48 | + body is read to EOF instead of trusting either length, preventing a |
| 49 | + response-smuggling desynchronization. |
| 50 | + |
| 51 | + *) Bugfix: keep downstream keepalive disabled after a duplicate or invalid |
| 52 | + upstream "Content-Length"; a clean upstream close no longer re-enables |
| 53 | + it, so an HTTP/1.1 client cannot reuse the connection past the re-framed |
| 54 | + response. The complete body still ends with its terminal chunk — body |
| 55 | + truncation is now tracked separately from the inconsistent flag. |
| 56 | + |
| 57 | + *) Bugfix: bound the request-header fields region against the shared |
| 58 | + memory message size in libunit, validate every field name/value |
| 59 | + serialized pointer and the cached header-field indexes before use, |
| 60 | + and range-check off/len in the Java InputStream read() binding, |
| 61 | + preventing out-of-bounds access on a malformed request. |
| 62 | + |
| 63 | + *) Bugfix: fix a libunit port-fd double-close race in the Go language |
| 64 | + module: getUnixConn() wrapped the raw shared-port descriptor (which |
| 65 | + net.FileConn dups) and a deferred Close() closed the original |
| 66 | + descriptor number while libunit still owned it, an intermittent |
| 67 | + double-close that raised an EBADF alert or, worse, closed an |
| 68 | + unrelated reused live descriptor. Go now dup(2)s the descriptor and |
| 69 | + wraps only the dup, so libunit remains the sole owner and closer; |
| 70 | + libunit additionally holds a port reference across the out-of-mutex |
| 71 | + add_port callback window so a concurrent destroy cannot free the |
| 72 | + port mid-callback. |
| 73 | + |
| 74 | + *) Feature: SIGQUIT now performs a graceful stop — application workers |
| 75 | + drain in-flight requests before exiting; SIGTERM remains the fast |
| 76 | + exit that drops them. |
| 77 | + |
| 78 | + *) Change: upgrade the wasm-wasi-component wasmtime crates from 35.0.0 |
| 79 | + to 36.0.12, clearing the wasmtime sandbox-escape advisories, with the |
| 80 | + accompanying WasiView/WasiHttpView source migration. |
| 81 | + |
| 82 | + *) Change: upgrade contrib njs to 1.0.0. |
| 83 | + |
| 84 | + *) Feature: two-phase listener close — reconfiguration disarms a removed |
| 85 | + listener first and releases its descriptor only after pending accept |
| 86 | + references drain, instead of resetting them. |
| 87 | + |
| 88 | + *) Change: the event engine now tracks in-flight connections on a |
| 89 | + dedicated active connections queue, symmetric with idle tracking — |
| 90 | + groundwork for graceful connection draining; connection accounting |
| 91 | + in the "/status" API is exact on all close paths. |
| 92 | + |
| 93 | + *) Change: broaden the regression test suite — proxy Content-Length and |
| 94 | + chunked-response relay framing, OpenTelemetry 5xx tracing and inbound |
| 95 | + traceparent handling, and Java ServletInputStream bounds — and run |
| 96 | + the C unit-test suite in CI, which was previously compiled but never |
| 97 | + executed. |
| 98 | + |
| 99 | + *) Bugfix: authorize privileged IPC message types by the kernel-validated |
| 100 | + sender PID (SCM_CREDENTIALS), close any received descriptors on every |
| 101 | + rejected privileged message, and cap port queue item size at the slot |
| 102 | + data bound. |
| 103 | + |
| 104 | + *) Bugfix: reject control characters (CR, LF, NUL, DEL) in "set_headers" |
| 105 | + names and values at configuration load, including the literal |
| 106 | + segments of templated values. |
| 107 | + |
| 108 | + *) Bugfix: handle buffer-allocation failure in the port read handlers |
| 109 | + with an orderly teardown instead of a NULL-pointer dereference, route |
| 110 | + a connection write error after a partial send to the error handler |
| 111 | + immediately, and free the timer batching array on engine teardown. |
| 112 | + |
| 113 | + *) Feature: upgrade the OpenTelemetry Rust crates from 0.24 to 0.32 and |
| 114 | + rewrite the OTLP trace exporter on the stable, dedicated-thread batch |
| 115 | + span processor (blocking reqwest client, no tokio runtime). |
| 116 | + |
| 117 | + *) Feature: W3C traceparent header inheritance now correctly propagates |
| 118 | + the upstream sampling decision; the sampler wraps TraceIdRatioBased |
| 119 | + inside ParentBased so that remote parent trace flags are honoured. |
| 120 | + |
| 121 | + *) Feature: add fake_otlp mock OTLP collector (test/fake_otlp/) and the |
| 122 | + test_otel.py test suite covering span export, traceparent |
| 123 | + propagation, sampling, and configuration validation over both the |
| 124 | + OTLP/HTTP and OTLP/gRPC transports. |
| 125 | + |
| 126 | + *) Feature: reintroduce OTLP/gRPC trace export. Both transports are |
| 127 | + compiled into every "--otel" build and the |
| 128 | + "settings/telemetry/protocol" option selects "http" (OTLP/HTTP) or |
| 129 | + "grpc" (OTLP/gRPC) at runtime, as in upstream Unit. gRPC uses tonic |
| 130 | + over a small tokio runtime; v1 is plaintext h2c only (no TLS to the |
| 131 | + collector, like the HTTP path). |
| 132 | + |
| 133 | + *) Change: rename the default OTel service name from "NGINX Unit" to |
| 134 | + "FreeUnit". |
| 135 | + |
| 136 | + *) Bugfix: add validation bounds for "batch_size" (1-65536) and |
| 137 | + "sampling_ratio" (0-1) in the OpenTelemetry configuration; previously |
| 138 | + invalid values were silently rejected without an error message. |
| 139 | + |
| 140 | + *) Bugfix: replace eprintln! error output in the OTel Rust layer with |
| 141 | + the C log callback (nxt_otel_log_cb), so errors flow through the |
| 142 | + standard Unit logging infrastructure. |
| 143 | + |
| 144 | + *) Bugfix: remove unreachable Protocol::HttpJson dead-code arm from the |
| 145 | + OTel exporter; only HttpBinary was ever selected. |
| 146 | + |
| 147 | + *) Bugfix: fix a rootfs automount reload race: sever the worker's mount |
| 148 | + namespace propagation (MS_REC|MS_PRIVATE) before mounting and let a |
| 149 | + per-worker automount be reaped with the namespace instead of being |
| 150 | + unmounted from the host, so a configuration reload no longer races the |
| 151 | + previous worker generation's teardown against the new prototype's |
| 152 | + procfs mount (freeunitorg/freeunit#83). |
| 153 | + |
| 154 | + *) Bugfix: validate that a loaded TLS private key matches its certificate |
| 155 | + at config time, and guard the wildcard-name SAN matcher against a |
| 156 | + zero-length SAN entry. |
| 157 | + |
| 158 | + *) Bugfix: correct IPv4 /32 CIDR fallthrough, symmetric URI/pattern |
| 159 | + decoding, the PCRE2 match-data ovector size, and short port-range |
| 160 | + parsing in HTTP routing; document the case-insensitive host matcher |
| 161 | + and the capset stance. |
| 162 | + |
| 163 | + *) Bugfix: tighten file-descriptor and CLOEXEC lifetime — CLOEXEC-protect |
| 164 | + accepted sockets and pipe ends, close the pipe end on |
| 165 | + nxt_fd_nonblocking() failure and the source fd on compression mmap |
| 166 | + failure, and narrow the accept4() fallback to ENOSYS. |
| 167 | + |
| 168 | + *) Bugfix: tighten WebSocket frame-bound checks — reject truncated |
| 169 | + extended-length frames in libunit, validate the 64-bit extended-length |
| 170 | + MSB (RFC 6455), fix a no-op frame-size decrement that could copy bytes |
| 171 | + beyond the declared payload, bound the Java sendWsFrame JNI arguments, |
| 172 | + and guard pending_payload_len overflow in the Python ASGI handler. |
| 173 | + |
| 174 | + *) Bugfix: bounds-check peer-supplied shared-memory offsets — range-check |
| 175 | + chunk_id and chunk_id+nchunks in incoming mmap messages, close a |
| 176 | + lookup/dereference window on the incoming-mmap handler, reject |
| 177 | + response-buffer size overflow, and validate request sptr offsets |
| 178 | + before use. |
| 179 | + |
| 180 | + *) Bugfix: bounds-check app-supplied arguments across the language |
| 181 | + bindings (PHP header skip / realpath / PATH_INFO; Python WSGI and ASGI |
| 182 | + checks; Perl ERRSV scrub; Java InputStream.readLine off/len; WASM |
| 183 | + guest offsets). |
| 184 | + |
| 185 | + *) Bugfix: tighten isolation boundaries — require a matching peer UID |
| 186 | + (root or Unit's own effective UID) on the unix control socket, so the |
| 187 | + "control-user", "control-group", and "control-mode" permissions are |
| 188 | + defense-in-depth rather than a means to delegate control-API access; |
| 189 | + resolve mount destinations with openat2(RESOLVE_BENEATH); and resolve |
| 190 | + relative cgroup paths against the child's /proc/<pid>/cgroup. |
| 191 | + |
| 192 | + *) Bugfix: reject embedded NUL bytes in the "rootfs" and cgroup "path" |
| 193 | + isolation options, and validate "rootfs" as an absolute path other |
| 194 | + than "/" at configuration time. |
| 195 | + |
| 196 | + *) Bugfix: fix an ineffective truncation guard when building the |
| 197 | + cgroup.procs pathname: snprintf()'s return value overwrote the |
| 198 | + original directory length before the bounds comparison, so a long |
| 199 | + cgroup directory path could overflow the check. The append now |
| 200 | + preserves the original length and fails with ENAMETOOLONG when |
| 201 | + "/cgroup.procs" plus the trailing NUL cannot fit. |
| 202 | + |
| 203 | + *) Bugfix: fail cgroup setup when the per-process pool allocation that |
| 204 | + caches the created directory for cleanup fails; the directory is now |
| 205 | + removed and setup errors out, rather than moving the process into a |
| 206 | + cgroup that could never be cleaned up — a cgroup directory leak under |
| 207 | + memory pressure. |
| 208 | + |
| 209 | + *) Bugfix: reject a "rootfs" that lexically resolves to "/" (such as |
| 210 | + "/.", "/..", or "/foo/.."); chroot("/") is a no-op and would silently |
| 211 | + defeat rootfs isolation. |
| 212 | + |
| 213 | + *) Bugfix: cap JSON parser depth and element counts in the controller, |
| 214 | + scrub PHP TrueAsync exception state before the prototype fork, and bind |
| 215 | + the Ruby rack.input / rack.errors handles to their originating request. |
| 216 | + |
| 217 | + *) Bugfix: bound proxy Content-Length and URI/string helpers — truncate a |
| 218 | + proxied response body that exceeds its Content-Length and close the |
| 219 | + connection, flag invalid or oversized upstream Content-Length, fix an |
| 220 | + nxt_is_complex_uri_encoded() off-by-one, and reject over-long lengths |
| 221 | + in nxt_rmemstrn(). |
| 222 | + |
| 223 | + *) Feature: enforce the runtime/OS end-of-life policy automatically — the |
| 224 | + unit-eol-check tool now fails when a shipped variant has outlived its |
| 225 | + support window (supported_until past EOL + grace), and a new |
| 226 | + eol-check GitHub workflow validates pkg/eol.json against endoflife.date |
| 227 | + on every relevant pull request and weekly on a schedule. |
| 228 | + |
| 229 | + |
| 230 | +Changes with FreeUnit 1.35.5 29 May 2026 |
| 231 | + |
| 232 | + *) Feature: automatically convert chunked request bodies to Content-Length |
| 233 | + when forwarding to upstream servers via proxy action. This enables |
| 234 | + compatibility with backends that do not support Transfer-Encoding: |
| 235 | + chunked (e.g., Gitea, servers requiring Content-Length). Fixes |
| 236 | + freeunitorg/freeunit#58, resolves nginx/unit#445 (client chunked), |
| 237 | + nginx/unit#1088 (duplicate TE), and nginx/unit#1278 (RFC 9112 epic). |
| 238 | + |
| 239 | + *) Change: chunked_transform feature is no longer experimental. Chunked |
| 240 | + request bodies can be accepted and transparently converted to |
| 241 | + Content-Length via configuration: { "settings": { "http": |
| 242 | + { "chunked_transform": true } } } |
| 243 | + |
| 244 | + *) Bugfix: fix TLS library busy-loop on peer-initiated close in SSL_write |
| 245 | + when connection is aborted by remote peer; prevents high CPU usage and |
| 246 | + ensures proper connection cleanup. |
| 247 | + |
| 248 | + *) Feature: add unfreeze-sync.sh script for automated migration of issues |
| 249 | + from nginx/unit to freeunitorg/freeunit with label mapping, deduplication, |
| 250 | + and dry-run preview support. |
| 251 | + |
| 252 | + *) Change: upgrade contrib njs to 0.9.8. |
| 253 | + |
| 254 | + *) Bugfix: fix mem-pool retain leak in cert/script-store IPC paths |
| 255 | + (router side) and fd/buffer leaks in cert/script/socket/access-log |
| 256 | + reply paths and the controller config-store path (main process |
| 257 | + side); all reachable when nxt_port_msg_alloc fails inside the |
| 258 | + port machinery. |
| 259 | + |
| 260 | + |
1 | 261 | Changes with FreeUnit 1.35.4 30 Apr 2026 |
2 | 262 |
|
3 | 263 | *) Security: all official Docker images now ship |
|
0 commit comments