Skip to content

fix(asap,gateway): inbound envelope OOM free and 1 MiB POST /asap cap - #97

Merged
adriannoes merged 7 commits into
developmentfrom
fix/bugbot-batch-asap
Sep 21, 2026
Merged

adriannoes merged 7 commits into
developmentfrom
fix/bugbot-batch-asap

Conversation

@adriannoes

Copy link
Copy Markdown
Collaborator

Summary

  • Rewrite against current development (not a cherry-pick of the Cursor drafts).
  • fill_response_envelope transfers payload ownership to the envelope so a failed required-field strdup cleanup does not cJSON_Delete the same object twice (local owned_payload; no payload = NULL, so cppcheck uselessAssignmentPtrArg stays clean).
  • POST /asap skips the leftover 64 KiB static Content-Length check when use_dyn_body is set, so envelopes in (64 KiB, 1 MiB] reach parse. test_asap_body_over_max now 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_server
  • GATEWAY=1 make test_asap_http_body / ./build/test_asap_http_body
  • GATEWAY=1 make test_gateway_http / ./build/test_gateway_http
  • CI=true make test
  • make static

Made with Cursor

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.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_payload in src/asap/server.c (fill_response_envelope) is a no-op alias (owned_payload = payload; out->payload = owned_payload). The real fix is dropping the extra cJSON_Delete so asap_envelope_clear owns the object once. out->payload = payload plus the existing comment is enough; the local is only there to avoid cppcheck uselessAssignmentPtrArg on payload = 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.md still describes only the 64 KiB response reject (RESP_BUF_SIZE). Inbound is ASAP_BODY_MAX (1 MiB). Worth one line so the leftover 64 KiB check is not reintroduced.
  • Per-IP /asap rate limit is still 10/min and still runs in handle_asap after the dyn malloc. A CL > 1 MiB 413 at init does not increment the counter. test_gateway_http now uses 7 counted POSTs in the 60s window (the over-max case is LWS 413, not counted). Headroom is 3 if more /asap cases are added.

Positive Highlights

  • Ownership is the right model: attach payload to out, let asap_envelope_clear free it. The new test_response_builder_missing_sender_does_not_double_free hits the same cleanup as post-attach OOM (HTTP parse always supplies sender/recipient). ASan with instrumented server.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_cap keeps the 64 KiB static cap for other POST/PUT. Fixing test_asap_body_over_max from CL 1000001 (still under 1048576) to 1048577 is 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 parse rpc_id double-free).

No merge blockers from this review.

Open in Web View Automation 

Sent by Cursor Automation: Adrianno’s personal code review

Bring in #96 shell hang fix and #98 atomic auth/skill persist.
Keep Unreleased changelog entries from all three batches.
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.
@adriannoes

Copy link
Copy Markdown
Collaborator Author

Addressed the Nice to Have items from the personal review (no inline threads).

  1. owned_payload alias — out->payload = payload plus the existing ownership comment is enough. Dropped the no-op local in src/asap/server.c. make static stays clean (the old cppcheck uselessAssignmentPtrArg was on payload = NULL, not on this assignment). c261e4571040c2c8f63ade0477ba954a0c189366

  2. Fail-closed on NULL — asap_http_body_exceeds_static_cap(NULL, …) now returns 1 instead of skipping the cap. bfd96851e8f73a4c1387dee67e455df4dc909a94

  3. Inbound 1 MiB docs — docs/ASAP.md POST /asap row now states inbound uses ASAP_BODY_MAX (1 MiB) and must not reintroduce the 64 KiB BODY_BUF_SIZE check. 7c3d1e27237c7455127d5d2f041467fdc7519fbc

  4. Rate-limit headroom — did not change ASAP_RATE_LIMIT_RPM (still 10/min). Recorded the review’s count in test_gateway_http.c: seven POSTs reach handle_asap; the 1 MiB 413 is LWS init and is not counted; headroom is 3. 5c214113970609cff96e3cf82d43e41bccbd9fc1

@adriannoes
adriannoes merged commit 9001648 into development Sep 21, 2026
4 checks passed
@adriannoes
adriannoes deleted the fix/bugbot-batch-asap branch September 21, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant