-
Notifications
You must be signed in to change notification settings - Fork 239
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
Introduce VoteState::deserialize_into_uninit #2272
Introduce VoteState::deserialize_into_uninit #2272
Conversation
a716f0e
to
460566b
Compare
self reminder: add SAFETY markers to all unsafe blocks |
This PR breaks the API (the breakage is warranted IMO), and now SPL tests are failing. But this is chicken and egg? How do I land this PR if the SPL tests fail, but how do I fix the SPL tests for the new API if I can't land this? Maybe I could add a new |
I've done this now |
02b33bb
to
e00e03b
Compare
had to read the docs on
just want to be sure i understand everything right since |
Correct! Both in the
Correct.
Yes it's meant to always be safe as long as you use safe rust, independent from what's inside the buffer. |
also mind that I haven't added a test, but this fixes deserialization for the following case:
In master this will incorrectly append votes, authorized_voters etc twice. With this PR the 2nd |
e00e03b
to
d78c70b
Compare
@ryoqun just tagged you too for review to double check the unsafe bits if you can |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still reviewing the rest, this was the first thing that caught my eye.
f0c7d92
to
38be6b8
Compare
sdk/program/src/vote/state/mod.rs
Outdated
std::ptr::drop_in_place(vote_state); | ||
} | ||
|
||
VoteState::deserialize_into_ptr(input, vote_state) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Self::deserialzie_into_ptr()
? (i.e. can use Self
).
|
||
match cursor.fill_buf() { | ||
Ok(buf) if buf.len() >= PUBKEY_SIZE => { | ||
// Safety: `buf` is guaranteed to be at least `PUBKEY_SIZE` bytes long |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: how about commenting that this safety also rely on the fact align = 1
for Pubkey
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
fyi, it looks like miri passes: f9e974f (need to severely reduce the loop counts due to miri being too slow.. lol)
|
354d61f
to
04cb1c3
Compare
vote_state: &mut VoteState, | ||
) -> Result<(), InstructionError> { | ||
let epoch_credit_count = read_u64(cursor)?; | ||
) -> Result<Vec<(u64, u64, u64)>, InstructionError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the first u64
is Epoch
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
cursor: &mut Cursor<&[u8]>, | ||
pubkey: *mut Pubkey, | ||
) -> Result<(), InstructionError> { | ||
const PUBKEY_SIZE: usize = mem::size_of::<Pubkey>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: strictly speaking, i think solana_program::pubkey::PUBKEY_BYTES
is more appropriate than this because this code is dealing with reading N bytes from the buffer. however, mem::size_of::<Pubkey> == PUBKEY_BYTES
(no padding or whatsoever). and i don't see any possibility of diverting the equality in foreseeable future. so quite minor thing ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
sdk/program/src/vote/state/mod.rs
Outdated
// Safety: | ||
// | ||
// Deserialize failed so at this point vote_state is uninitialized. We must write a | ||
// new _valid_ value into it or after returning from this function the caller is | ||
// left with an uninitialized `&mut VoteState`, which is UB (references must always | ||
// be valid). | ||
// | ||
// This is always safe and doesn't leak memory because deserialize_into_ptr() writes | ||
// into the fields that heap alloc only when it returns Ok(). | ||
unsafe { | ||
vote_state.write(VoteState::default()); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks okay.
nit: however, for completeness, similar cleaning is desired when panicking. that said, i couldn't think of panic arising from VoteState::deserialize_into_ptr()
. so, just mentioning about no panic will be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep nice catch, fixed the panic case too
just curious but i wonder how the CUs is reduced after this patch to save kittens. :) |
that'd be interesting, but it's not why I'm doing this. I'm doing this to fix
|
Deserializing into MaybeUninit<VoteState> saves the extra cost of initializing into a value initialized with VoteState::default()
Introduce new ::deserialize_into_uninit that takes MaybeUninit<VoteState>. This way we don't break API, and we can less abruptly deprecate ::deserialize_into and later on rename ::deserialize_into_uninit to ::deserialize_into.
…eserializing This is needed to deallocate votes, authorized_voters and epoch_credits
- explain why addr_of_mut!() is needed - minimize code inside unsafe blocks - tweak type definition in pointer cast
Avoids a (small) memcpy per key
On failure we must ensure that `vote_state` is left in a valid state to avoid UB.
6e3333c
to
339ce8c
Compare
339ce8c
to
5651ee3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm with nits.
the bench numbers looks promising. looking forward to see a follow-up pr for actual perf gains.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm with no nit (really thanks for addressing all the review comments)
thanks a lot for the thorough reviews! |
Deserializing into
MaybeUninit<VoteState>
saves the extra cost of initializing into a value initialized with VoteState::default().I am planning to use this to speed up VoteAccount::try_from() in a followup PR.