Skip to content

Commit 1245fa0

Browse files
committed
bugfix: prevent SIGSEGV in receiveuntil __gc on aborted multipart upload.
A client that POSTs a multipart body and aborts the connection mid-pattern against ngx.req.socket():receiveuntil(boundary) can SIGSEGV the worker from the LuaJIT GC's __gc finalizer on the compiled-pattern userdata. ngx_http_lua_socket_read_error_retval_handler calls ngx_http_lua_socket_tcp_finalize_read_part directly when the receiveuntil iterator's recv returns an error (e.g. client RST). finalize_read_part clears u->buf_in / u->bufs_in and memzeros u->buffer, but does not detach the compiled-pattern userdata held in u->input_filter_ctx -- so cp->upstream stays pointing at u and cp->state stays > 0 (the DFA stopped mid-match). Later, when LuaJIT GC sweeps the cp userdata, ngx_http_lua_socket_cleanup_compiled_pattern fires and calls ngx_http_lua_socket_tcp_read_prepare(r, u, NULL). cp->state > 0 forces the recovery branches that read u->buf_in->buf->pos -- a NULL deref. Fix: in finalize_read_part, clear cp->upstream mirroring the same detach that ngx_http_lua_socket_tcp_finalize already performs. This short-circuits the __gc handler at its existing `if (u != NULL)` guard, so the bad code path is never entered. Reproducer: POST a multipart body that ends mid-boundary, then close the socket with SO_LINGER {1,0} so the server reads RST while the DFA is mid-match. Lua handler does: local sock = ngx.req.socket() local iter = sock:receiveuntil("--" .. BOUNDARY) while true do local d = iter(1) if not d then break end end collectgarbage("collect") With the body shaped as six leading dashes against a 12-dash boundary leader, cp->state lands at 6 with no DFA fallback, and the synchronous collectgarbage runs cp's __gc inside the request -- 100% crash rate. Crash signature: #0 ngx_http_lua_socket_tcp_read_prepare (data=0x0) [inlined memcpy at ngx_http_lua_socket_tcp.c, recovery branch, u->buf_in == NULL] #1 ngx_http_lua_socket_cleanup_compiled_pattern ngx_http_lua_socket_tcp.c #2 lj_BC_FUNCC buildvm_x86.dasc #3 gc_call_finalizer lj_gc.c #4 gc_finalize lj_gc.c #5 gc_onestep lj_gc.c #6 lj_gc_fullgc lj_gc.c #7 lua_gc (what=LUA_GCCOLLECT) lj_api.c #8 lj_cf_collectgarbage lib_base.c #9 lj_BC_FUNCC buildvm_x86.dasc #10 ngx_http_lua_run_thread ngx_http_lua_util.c #11 ngx_http_lua_socket_tcp_resume_helper ngx_http_lua_socket_tcp.c #12 ngx_http_lua_socket_tcp_read ngx_http_lua_socket_tcp.c #13 ngx_http_request_handler src/http/ngx_http_request.c
1 parent 41ed26b commit 1245fa0

1 file changed

Lines changed: 8 additions & 0 deletions

File tree

src/ngx_http_lua_socket_tcp.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4203,6 +4203,14 @@ ngx_http_lua_socket_tcp_finalize_read_part(ngx_http_request_t *r,
42034203
ngx_memzero(&u->buffer, sizeof(ngx_buf_t));
42044204
}
42054205

4206+
/* Mirror ngx_http_lua_socket_tcp_finalize: detach the compiled-pattern
4207+
* userdata so a later __gc on it won't re-enter read_prepare on a
4208+
* half-finalised upstream. */
4209+
if (u->input_filter_ctx != NULL && u->input_filter_ctx != u) {
4210+
((ngx_http_lua_socket_compiled_pattern_t *)
4211+
u->input_filter_ctx)->upstream = NULL;
4212+
}
4213+
42064214
if (u->raw_downstream || u->body_downstream) {
42074215
if (r->connection->read->timer_set) {
42084216
ngx_del_timer(r->connection->read);

0 commit comments

Comments
 (0)