-
Notifications
You must be signed in to change notification settings - Fork 32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't retain a scalar that's inline in a mutable collection #223
Comments
snej
added a commit
that referenced
this issue
Jun 6, 2024
This is a bandaid for a nasty architectural bug.
snej
added a commit
that referenced
this issue
Jun 7, 2024
This is a bandaid for a nasty architectural bug; see the Github issue. Retaining still doesn't work, but it now throws an exception instead of overwriting out-of-bounds memory.
snej
added a commit
that referenced
this issue
Aug 5, 2024
This is a bandaid for a nasty architectural bug; see the Github issue. Retaining still doesn't work, but it now throws an exception instead of overwriting out-of-bounds memory.
snej
added a commit
that referenced
this issue
Aug 7, 2024
This is a bandaid for a nasty architectural bug; see the Github issue. Retaining still doesn't work, but it now throws an exception instead of overwriting out-of-bounds memory.
jianminzhao
pushed a commit
that referenced
this issue
Oct 7, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is kind of an ugly architectural issue I discovered recently. I don't think it's been seen in the wild.
The bug
Result: UBSan warnings about unaligned pointers, followed by an ASan fatal buffer overrun error. If sanitizers aren't enabled, you probably crash with heap corruption sometime later.
What's Going On
A value in a mutable container is stored in a ValueSlot, either inline or as a pointer. Small scalars (7 bytes or less) are stored inline. On little-endian CPUs, inline values are offset by 1 byte so that the first byte can be a 0xFF tag which marks it as inline. (If it were a pointer, this would be the low byte, which would be even since it's malloced.) That means an inline scalar Value has an odd address.
Unfortunately, an odd address usually denotes a heap-allocated mutable Value, i.e. a larger scalar or a collection. The HeapValue class deliberately offsets its embedded Value by one byte to ensure this. This is how Value::isMutable works.
The problem is that retaining a value [in HeapValue::retain] first checks isMutable; if so, it casts it to HeapValue and calls retain() on that. This incorrectly happens when the value is an inline scalar; but since it isn't actually a HeapValue, this ends up incrementing a 32-bit int 5 bytes before the Value, which is guaranteed to corrupt something. Ouch.
Workaround
Don't call
FLValue_Retain
on a value that might be a small scalar in a mutable collection.Band-Aid Fix
I have a patch that doesn't really fix the problem, but at least allows you to tell that an inline scalar isn't itself mutable, and will cause the Retain call to throw an exception instead of corrupting data.
A Real Fix?
Instead of throwing an exception, the Retain call should cast the Value to a ValueSlot pointer, then (somehow) find the ValueSlot's owning mutable collection, then retain that.
The "(somehow)" is the hard part. ValueSlot is intentionally tiny, 8 bytes. Adding a pointer back to its owner would double its size. But there's not really any other way to find it! The ValueSlots are the elements of either a
vector
or anunordered_map
owned by the HeapValue.The text was updated successfully, but these errors were encountered: