Skip to content

fix(providers): fail closed when Anthropic parse realloc fails - #80

Merged
adriannoes merged 2 commits into
developmentfrom
cursor/high-severity-issues-3c61
Sep 13, 2026
Merged

adriannoes merged 2 commits into
developmentfrom
cursor/high-severity-issues-3c61

Conversation

@cursor

@cursor cursor Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Summary

  • Anthropic parse_response_body now publishes text_cap / tool_cap only after realloc succeeds. Grow failure fails the parse, frees scratch state, and sets an OOM error instead of memcpy/indexing against an inflated cap.
  • Reimplemented on current development (includes fix(asap): avoid double-free on malformed JSON-RPC results聽#84). Not a cherry-pick of the original Bot commit.
  • Isolated to src/providers/anthropic.c and tests/test_anthropic.c. The original Bot PR failed make static (variableScope on size_t i).

Test plan

  • test_parse_text_past_initial_cap (300-byte text grows past 256)
  • test_parse_six_tool_use_blocks (6th tool_use grows past cap 4)
  • test_parse_text_realloc_failure_is_error (red: success/overflow, then green fail-closed)
  • test_parse_tool_realloc_failure_is_error
  • make test_anthropic / ASan+UBSan on test_anthropic
  • CI=true make test
  • make static

Refs: #80


Note

Low Risk
Scoped to Anthropic response parsing and test-only realloc hooks; behavior change is stricter error handling on OOM instead of possible buffer misuse.

Overview
Fixes #80: Anthropic content parsing no longer bumps text_cap / tool_cap before realloc succeeds, which could let memcpy or tool indexing run against a larger cap than the allocated buffer.

Parsing now grows text and tool_use arrays through helpers that only publish capacity after a successful grow, return -1 with an OOM message on failure, and tear down partial state via parse_fail. Under SHELLCLAW_TEST, anthropic_test_fail_next_reallocs simulates failed grows for regression tests (long text past 256 bytes, six tool_use blocks, and explicit realloc-failure paths). CHANGELOG documents the fix.

Reviewed by Cursor Bugbot for commit e489f0b. Bugbot is set up for automated code reviews on this repo. Configure here.

Publish text_cap/tool_cap only after realloc succeeds. On grow failure,
free parse scratch and return an error instead of memcpy/index against
an inflated cap.

Refs: #80
@adriannoes
adriannoes force-pushed the cursor/high-severity-issues-3c61 branch from f2a60f6 to e489f0b Compare September 13, 2026 06:26
@adriannoes
adriannoes changed the base branch from main to development September 13, 2026 06:27
@adriannoes
adriannoes marked this pull request as ready for review September 13, 2026 06:27
@adriannoes
adriannoes self-requested a review as a code owner September 13, 2026 06:27

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Not approved: Bugbot and Security Agent both passed with no findings, but GitHub blocked this automation from approving its own PR. Human review is still required; adriannoes is already assigned and no additional reviewers were added.

Open in Web聽View Automation聽

Sent by Cursor Approval Agent: Pull Request Router and Approver

@adriannoes adriannoes self-assigned this Sep 13, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Summary

ShellClaw is a C99/C11 edge agent (Jetson / Raspberry Pi) with a ReAct loop over LLM providers, tools, channels, an embedded gateway, and ASAP. Providers sit behind provider_t and share curl/JSON helpers in provider_common.c.

This PR fixes a real heap misuse in Anthropic parse_response_body: the old loop did text_cap *= 2 / tool_cap *= 2 before realloc, then memcpy or indexed tool_calls[tool_count] against the inflated cap when the grow failed. The new ensure_text_cap / ensure_tool_cap helpers publish capacity only after anth_realloc succeeds; failure goes through parse_fail (scratch freed, OOM error) instead of copying into a 256-byte block with a 512-byte cap. Scope is src/providers/anthropic.c, tests/test_anthropic.c, and a CHANGELOG line. That is the right layer and the right size.

No merge blockers. I re-ran make static (cppcheck 2.13), CI=true test_anthropic (-Werror), and GCC ASan+UBSan with detect_leaks=1 on ./build/test_anthropic. All passed. GitHub static, test, release was still in progress at review time.

Must Fix

None.

Should Fix

None.

Nice to Have

  1. ensure_tool_cap (src/providers/anthropic.c 83-104) doubles once, while ensure_text_cap loops until *text_cap >= need. Safe today because append_tool_use only ever asks for *tool_count + 1. A while (tool_count >= *tool_cap) grow would make the helper correct if a later caller jumps the count.
  2. test_parse_text_realloc_failure_is_error (tests/test_anthropic.c 215-233) does not assert response.tool_calls == NULL / tool_calls_count == 0. The tool-OOM test does. A regression that published a stale tool array on text OOM would not be caught.
  3. append_tool_use still returns 0 if provider_dup_str or cJSON_PrintUnformatted returns NULL (src/providers/anthropic.c 139-146). The parse succeeds with a NULL id/name/args. Pre-existing; agent.c treats a NULL name as an unknown tool. Not the overflow this PR claims to fix.
  4. s_fail_next_reallocs is reset by each OOM test after the parse. Resetting it inside anthropic_parse_response_for_test would stop a forgotten hook from poisoning a later case.

Positive Highlights

  • Cap is published only after realloc, using a temp grown pointer, so a failed grow cannot NULL the original block or lie about its size. That matches provider_write_cb in provider_common.c (42-45).
  • parse_fail frees each id/name/arguments up to tool_count before freeing the array, so a failed 5th tool_use does not leak the first four.
  • Tests cover both success growth (300-byte text past 256, 6 tool_use past cap 4) and injected realloc failure. The hook is #ifdef SHELLCLAW_TEST only; production anthropic.o is a plain realloc.
  • CHANGELOG Unreleased Fixed line is in its own docs commit. OpenAI/local stay on provider_parse_chat_completions_json, which mallocs the exact JSON array size, so this grow-then-index bug was Anthropic-specific.
  • parse_fail uses for (size_t i = 0; rather than a function-scope i, which is the variableScope hit that failed make static on the earlier Bot attempt. cppcheck is clean on this tree.
Open in Web聽View Automation聽

Sent by Cursor Automation: Adrianno鈥檚 personal code review

@adriannoes
adriannoes merged commit c5fadc6 into development Sep 13, 2026
7 checks passed
@adriannoes
adriannoes deleted the cursor/high-severity-issues-3c61 branch September 13, 2026 06:49
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