fix(asap,gateway): inbound envelope OOM free and 1 MiB POST /asap cap - #97
Conversation
Transfer payload ownership to the envelope so a failed strdup cleanup does not cJSON_Delete the same object twice.
Skip the 64 KiB static buffer check when POST /asap uses the dynamic body so legal envelopes up to ASAP_BODY_MAX are not 413'd.
There was a problem hiding this comment.
Summary
Small, correct fix for two real bugs on protocol-public POST /asap. ShellClaw is a C99 edge agent; this PR stays in src/asap (inbound envelope builder) and src/gateway (libwebsockets body cap). fill_response_envelope no longer double-frees the payload cJSON when a required-field strdup fails. The leftover Content-Length > BODY_BUF_SIZE (64 KiB, shared with PUT /api/config) no longer 413s legal envelopes in (64 KiB, 1 MiB]. GitHub static, test, release is green. Locally: make static, test_asap_server under GCC ASan+UBSan, GATEWAY=1 test_asap_http_body, and GATEWAY=1 test_gateway_http (including the new ~70 KiB state.query) all passed.
Must Fix
None.
Should Fix
None for merge.
Nice to Have
owned_payloadinsrc/asap/server.c(fill_response_envelope) is a no-op alias (owned_payload = payload; out->payload = owned_payload). The real fix is dropping the extracJSON_Deletesoasap_envelope_clearowns the object once.out->payload = payloadplus the existing comment is enough; the local is only there to avoid cppcheckuselessAssignmentPtrArgonpayload = NULL.asap_http_body_exceeds_static_cap(NULL, ...)returns 0 (skip the cap). Production always passes&conn->body. Fail-closed on NULL would be safer if a later caller forgets.docs/ASAP.mdstill describes only the 64 KiB response reject (RESP_BUF_SIZE). Inbound isASAP_BODY_MAX(1 MiB). Worth one line so the leftover 64 KiB check is not reintroduced.- Per-IP
/asaprate limit is still 10/min and still runs inhandle_asapafter the dyn malloc. A CL > 1 MiB 413 at init does not increment the counter.test_gateway_httpnow uses 7 counted POSTs in the 60s window (the over-max case is LWS 413, not counted). Headroom is 3 if more/asapcases are added.
Positive Highlights
- Ownership is the right model: attach payload to
out, letasap_envelope_clearfree it. The newtest_response_builder_missing_sender_does_not_double_freehits the same cleanup as post-attach OOM (HTTP parse always supplies sender/recipient). ASan with instrumentedserver.o/envelope.o/ cJSON did not complain. - The 1 MiB cap was already allocated and documented in code; this PR makes the HTTP path match it. Extracting
asap_http_body_exceeds_static_capkeeps the 64 KiB static cap for other POST/PUT. Fixingtest_asap_body_over_maxfrom CL1000001(still under 1048576) to1048577is the actual cap test; the old number only 413'd because of the leftover 64 KiB check. - Two conventional commits, CHANGELOG Fixed + Security, no sandbox/config mix-in. Distinct from #61 (outbound JSON-RPC vs
RESP_BUF_SIZE) and #84 (client parserpc_iddouble-free).
No merge blockers from this review.
Sent by Cursor Automation: Adrianno’s personal code review
Assign payload directly to out->payload. The extra cJSON_Delete is already gone; the local alias was only for cppcheck.
Fail closed if a later caller forgets to pass &conn->body instead of skipping the 64 KiB check.
Keep the leftover 64 KiB static BODY_BUF_SIZE check off inbound /asap.
Seven counted POSTs in the 60s window leave 3 of 10 slots; the 1 MiB 413 path is LWS init and is not counted.
|
Addressed the Nice to Have items from the personal review (no inline threads).
|


Summary
development(not a cherry-pick of the Cursor drafts).fill_response_envelopetransfers payload ownership to the envelope so a failed required-fieldstrdupcleanup does notcJSON_Deletethe same object twice (localowned_payload; nopayload = NULL, so cppcheckuselessAssignmentPtrArgstays clean).POST /asapskips the leftover 64 KiB static Content-Length check whenuse_dyn_bodyis set, so envelopes in (64 KiB, 1 MiB] reach parse.test_asap_body_over_maxnow uses Content-Length 1048577 (ASAP_BODY_MAX + 1).Supersedes #93, #86.
Does not mix config_patch (#58) or sandbox.
Test plan
make test_asap_server/./build/test_asap_serverGATEWAY=1 make test_asap_http_body/./build/test_asap_http_bodyGATEWAY=1 make test_gateway_http/./build/test_gateway_httpCI=true make testmake staticMade with Cursor