The felt252.try_into()
implementation in byte_array_extra.cairo
is incorrect leading to felts being serialized into a format other than specified in cairo.
What the function does is take a single felt252
and convert it to a ByteArray
. If we look at the documentation of serialization in cairo (https://docs.starknet.io/architecture-and-concepts/smart-contracts/serialization-of-cairo-types/) we can look at the section Serialization of byte arrays
:
Most importantly note that The pending word consists of at most 30 bytes.
Now let's look at the implementation in byte_array_extra.cairo
:
We see that a new ByteArray
is created and self
(felt252
) is assigned to its pending_word
.
Now the max value for a felt252
is 3618502788666131213697322783095070105623107215331596699973092056135872020480
, which contains 252
bits. 252
bits can represent 31.5 bytes
(31.5 * 8 = 252
).
Here we see the problem which is that if we have a very big felt252
, exceeding 30 bits
, the function does not provide correct results anymore as the pending_word
will contain 31
or even 32
bytes (the half
byte at the top is taken a a full byte with the first 4 bits being zero).
This means that felts will be converted to ByteArray
s which are technically incorrectly serialized.
In order to show the incorrect serialization please add the following test to byte_array_extra.cairo
and execute it with snforge test from_felt252_wrong_serialization
.
Manual review
In order for try_into
to correctly serialize felts to byte arrays, the function would need to check whether the felt contains more than 30 bytes
and if so, the felt needs to be split into data
and pending_word
in the resulting byte array.
Great catch ! Unfortunatelly there is no impact... :/ I simulated the end of the calling functions and it seems that the "span" conversion handle that case. Here is my PoC: ``` fn input_31_byte() { let a: felt252 = 'qwertyuiopasdfghjklzxcvbnm12345'; let mut out_uris = array![]; let convert: Option<ByteArray> = a.try_into(); match convert { Option::Some(e) => { out_uris.append(e); }, Option::None => panic!("Should not be None") } let span = out_uris.span(); println!("Real data used in the calling function {:?}", span[0]); let b: ByteArray = "qwertyuiopasdfghjklzxcvbnm12345"; let mut out_uris_2 = array![]; out_uris_2.append(b); println!("Real data used in the calling function {:?}", out_uris_2.span()[0]); } ```
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.