Repository navigation
fix(standards): stop NoteExecutionHint::can_be_consumed overflowing - #3869
erkancamli wants to merge 4 commits into
Conversation
The hint shifted by an unvalidated u8, which panics in debug and has the shift masked in release. Rebased onto next; the CHANGELOG entry now appends to the existing Unreleased Fixes section instead of opening a second one.
b879b20 to
51bc63b
Compare
|
Small follow up offer on this one. The out of range behaviour is currently explained only in an inline comment; the public doc comments on I also want to be explicit about why the check sits at the evaluation site rather than at construction, since that is the obvious alternative. Rejecting out of range lengths in Worth knowing that out of range lengths are already constructed in tree: Finally, a heads up across #3867, #3868 and #3869: all three add a line to the same |
The previous revision saturated the slot bounds in u32. A slot can end at exactly 2^32, which does not fit a u32 even though every block inside the slot does, so saturating clamped that end to u32::MAX and answered false for BlockNumber::MAX, which the slot actually contains. on_block_slot(0, 0, 0) puts every block in its own slot, yet it answered false at u32::MAX. So the previous revision turned a debug panic into a silently wrong answer rather than the right one. Compute the bounds in u64 instead. Nothing can overflow there: the round product is at most block_num, and slot_offset is a u8 while both lengths are at most 2^31. Behaviour is unchanged for every block below the last one. Adds a test pinning the boundary, including the block before it, which both versions answer the same way.
The entry named only the shift overflow. It also fixes the slot_offset overflow, which panics in debug for round and slot lengths that are entirely in range, and the u32 saturation that answered false for BlockNumber::MAX.
|
Correcting a bug I introduced in my own fix, before anyone spends review time on it. The first revision saturated the slot bounds in let slot_end_block = slot_start_block.saturating_add(slot_len_blocks);A slot can end at exactly The simplest case is
So the first revision turned a debug panic into a silently wrong answer rather than into the right one, at exactly the boundary a reviewer would probe. That is worse than the bug it replaced, and it was mine. Now the bounds are computed in
I also widened the CHANGELOG entry. It named only the shift overflow, and this patch also fixes the One thing I have not been able to do myself: the repo pins Rust 1.98.1 and my sandbox cannot reach |
|
Thanks, but we're not accepting external contributions at this point. |
Third one in this area, independent of #3867 and #3868.
What
OnBlockSlotstoresround_lenandslot_lenasu8, so both can be 32 or more.can_be_consumedshifts by them unconditionally:A shift of 32 or more is not defined for
u32, so this panics with attempt to shift left with overflow in debug and silently masks the shift amount in release, where1 << 33becomes1 << 1. The same hint then answers differently depending on how the binary was built. The offset arithmetic can overflow in the same two ways onceslot_lenis large.These are not hypothetical values.
from_partsonly rejects a non-zero top byte of the payload, so any length in0..=255decodes fine, and this repo's owntest_encode_round_tripround tripsOnBlockSlot { round_len: 22, slot_len: 33, slot_offset: 44 }.NetworkAccountTarget::try_fromalso builds a hint straight out of an attachment word (NoteExecutionHint::from(word[2])), so the value can come from a note that someone else published.A wallet or scanner that filters candidate notes with
can_be_consumed(tip)therefore aborts on one such note, or, in release, mis-schedules it.Fix
checked_shlfor the lengths, andNonewhen the length does not fit.Nonealready means "we do not know whether this note can be consumed", which is exactly the situation: the hint cannot be evaluated.For the offset, saturating arithmetic. A slot that starts past the last block never comes around, so
Some(false)is the honest answer and no case overflows.I deliberately did not tighten
from_partsto reject lengths above 31, which would be the other way to fix this. That would change decoding, andtest_encode_round_tripshows you currently treat those payloads as valid encodings. If you would rather have the validation at the decode boundary, and that test updated, say so and I will send that instead.Test
out_of_range_block_slot_lengths_are_not_consumablecovers both lengths, the decoded path, and the offset overflow.Notes
Same environment caveats as #3867 and #3868: stable 1.95 with
--ignore-rust-versionbecause the pinned 1.98.1 toolchain is not reachable from here, and stablecargo fmt --checkdisagrees with this repo's nightly config across the crate, so I matched the surrounding style by hand. Please lean on CI for both.Happy to add the CHANGELOG entry once this has a number.