-
Notifications
You must be signed in to change notification settings - Fork 290
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
array_to_bounded_vec is dangerous #9854
Comments
I also noticed problematic usage in pending_read_hints: BoundedVec {
storage: [
PendingReadHint::nada(MAX_NULLIFIER_READ_REQUESTS_PER_TX); NUM_PENDING_HINTS
],
len: 0,
}, where |
Sounds like it just needs a rename? I think it's intentionally designed with the assumption that the data will be tightly packed to the "left". Agree it's not clear at the moment. |
I think this is because an empty (all zeroes) PendingReadHint is valid, I don't know if anything would break if changed to new() though |
Hey, is there any progress on this? The protocol circuits are making pretty heavy use of reading and writing to |
Could we have new_unchecked with storage and len in the same spirit as get_unchecked and set_unchecked? that would ease the migration |
Do they have to be unchecked? Would a checked fn from_parts(array: [T; MaxLen], len: u32) -> BoundedVec<T, MaxLen> {
assert(len <= MaxLen);
BoundedVec { storage: array, len }
} |
Unchecked in the sense that we don't iterate over the array again to verify that all items after |
That |
Right, forgot about that change. So there's a tradeoff here. I could change the API so that we no longer check the entire self.storage[0] == other.storage[0]
& self.storage[1] == other.storage[1]
...
& self.storage[MaxLen-1] == other.storage[MaxLen-1] to if 0 < self.len {
self.storage[0] == other.storage[0]
} &
if 1 < self.len {
self.storage[1] == other.storage[1]
} &
...
if MaxLen - 1 < self.len {
self.storage[MaxLen-1] == other.storage[MaxLen-1]
} |
That's going to make any equality checks on boundedvecs very expensive unless the length is known at compile time. |
Yes, that's the tradeoff. |
This is
utils::arrays::array_to_bounded_vec
, which is used throughout the kernel data builders:Note how
array
is not mutated at all: the implementation assumes that all empty values are located at the end of the array, and attempts to only count the non-empty ones. This is extremely dangerous for two reasons:a) if
array
were to have even a single empty value with a lower index than any non-empty value, the non-empty value would be lost, and BoundedVec's storage would be corruptb) if
T::empty()
returns true for any value that is notstd::mem::zeroed()
,BoundedVec
's storage will again be corrupt. This is becauseBoundedVec
assumes that unused storage elements are cleared, and blindly compares all ofstorage
in itseq
implementation. An empty value that is not zeroed would cause for the resultingBoundedVec
to not be equal to one that is created by inserting all of its elements into a new one, since the 'empty' storage would not be the same.Additionally,
array_to_bounded_vec
does not convey this handling of empty values, and seems to imply the function would behave the same asBoundedVec::from_array
, which it does not.I stumbled upon this when clearing warnings: this function emits some as
BoundedVec
'sstorage
andlen
properties are private as they are not meant to be directly accessed (due to issues like the ones described here).The text was updated successfully, but these errors were encountered: