Skip to content

fix(interface): validate Borsh string lengths in unpack() to prevent SBF heap abort#87

Merged
joncinque merged 2 commits into
solana-program:mainfrom
bit2swaz:fix/borsh-length-panic
Jun 29, 2026
Merged

fix(interface): validate Borsh string lengths in unpack() to prevent SBF heap abort#87
joncinque merged 2 commits into
solana-program:mainfrom
bit2swaz:fix/borsh-length-panic

Conversation

@bit2swaz

@bit2swaz bit2swaz commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Problem

TokenMetadataInstruction::unpack() calls try_from_slice(rest)? directly on attacker-controlled instruction data. A Borsh String is a little-endian u32 length prefix followed by that many bytes, so a forged prefix like u32::MAX makes Borsh try to allocate ~1 MiB before the ? error path runs and discovers the buffer is too short. On the 32 KiB SBF heap that allocation aborts the program instead of returning a clean error.

Only the variants that carry Borsh strings are affected: Initialize (name, symbol, uri), UpdateField (the value string, plus the nested string in Field::Key, tag 3), and RemoveKey (the key string after the idempotent bool). UpdateAuthority and Emit have no strings and are left alone.

Fix

A new check_borsh_string helper reads each string's declared length and checks it against the remaining buffer before try_from_slice runs, returning InvalidInstructionData when it doesn't fit. The three affected match arms walk a cursor through their fields and validate each string first. The length arithmetic is checked so it can't overflow.

Tests

Three tests feed a forged u32::MAX length prefix through unpack() for Initialize, UpdateField, and RemoveKey and assert InvalidInstructionData. They run natively, no SBF build needed. They also work as real regression guards: before the fix Borsh returns a different (IO) error, so the assertion would fail.

Context

This puts the validation in spl-token-metadata-interface, where the data is deserialized, rather than in the token-2022 caller. It follows the discussion on solana-program/token-2022#1194 (now closed), where @joncinque suggested the fix should live here. References solana-program/token-2022#1152. The token-2022 side is then just a dependency bump once this releases.

🤖 Generated with Claude Code

bit2swaz and others added 2 commits June 12, 2026 17:36
…SBF heap abort

Forged u32 length prefixes in Initialize/UpdateField/RemoveKey payloads could
trigger an oversized allocation in try_from_slice that aborts on the 32 KiB SBF
heap. Validate each string length against the remaining buffer before
deserializing, returning InvalidInstructionData for malformed payloads.

Closes solana-program/token-2022#1152

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The doc comment on check_borsh_string uses both words; cargo spellcheck
flagged them. They're a correct word and a known acronym (SBF sits
alongside TLV, APY, DAO already in the dict).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bit2swaz

bit2swaz commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@joncinque this is the spl-token-metadata-interface side of solana-program/token-2022 #1152 where youd suggested the fix should live after token-2022 #1194 was closed. mind taking a look?

@joncinque

joncinque commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

You just need (edit: needed) to rebase your branch, the fix was already done with that quinn-proto upgrade you mentioned

@bit2swaz

Copy link
Copy Markdown
Contributor Author

ah i see, thankyou. makes sense, the re-run from my spellcheck push already pulled in the quinn-proto fix, so Audit's green now. my apologies for the extra issue. all checks pass on this one now

@joncinque joncinque left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for your contribution!

It looks a bit hacky, but unless we switch to another serialization library like wincode, which allows you to specify max allocation sizes during deserialization, I don't think we have any better options.

@joncinque joncinque merged commit fc97c60 into solana-program:main Jun 29, 2026
22 checks passed
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.

2 participants