Skip to content

[verifier] Do not form out-of-bounds pointers for vector elements - #9463

Open
The-Peacemaker wants to merge 1 commit into
google:masterfrom
The-Peacemaker:verifier-vector-element-hardening
Open

The-Peacemaker wants to merge 1 commit into
google:masterfrom
The-Peacemaker:verifier-vector-element-hardening

Conversation

@The-Peacemaker

Copy link
Copy Markdown

The verifier's own contract is that a pointer is only ever formed once it is known to be inside the buffer. VerifyOffset already enforces this by checking the target arithmetically first.

VerifyVectorOfStrings and VerifyVectorOfTables did not follow the same discipline. Both resolved each element with vec->Get(i), whose IndirectHelper forms a pointer straight from the element offset before any bounds check. For a crafted buffer the verifier therefore materialises a pointer pointing arbitrarily far outside the buffer before rejecting it.

Nothing dereferences the result today, so release behaviour is unchanged, but the invariant is meant to hold even on rejection paths, and it is exactly the kind of thing UBSan builds that instrument pointer arithmetic will trip over (and that a future accessor reaching the element first would turn into a real read). This makes the vector paths obey the same rule VerifyOffset already applies everywhere else.

What this does

Adds VerifyVectorElement, which validates a vector element's offset with the existing VerifyOffset<uoffset_t> (which does not form a pointer until the target is proven in bounds) and returns the resolved pointer, or nullptr when the element is out of range. VerifyVectorOfStrings and VerifyVectorOfTables now go through it instead of vec->Get(i).

The 32-bit offset is correct for these two functions: both take Vector<Offset<T>>, whose element/offset type is uoffset_t. The 64-bit Vector64/Offset64 uses a different type and does not bind to these.

Tests

Adds VerifyVectorElementBoundsTest (tests/test.cpp), covering valid buffers verifying and string/table vector elements corrupted to 0xFFFFFFFF being rejected.

Behaviour

No behavioural change for well-formed buffers: same buffers verify. Corrupt offsets still fail verification, now without forming an out-of-bounds pointer on the way.

VerifyVectorOfStrings and VerifyVectorOfTables resolved each element through Vector::Get, whose IndirectHelper forms a pointer from the element offset before any bounds check. The verifier's own contract says a pointer must be proven inside the buffer before it is formed. For a crafted buffer that means the verifier materialises a pointer pointing (in the worst case) gigabytes past the end of the buffer before rejecting it. Behaviour today is safe in release (rejection still happens), but the invariant is meant to hold even on rejection paths; UBSan builds that instrument pointer arithmetic will flag this, and any future accessor that touches the element before the final check would turn it into a real read.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant