Skip to content

kernel: Avoid an out-of-range shift in CopyBits - #6534

Merged
fingolfin merged 1 commit into
masterfrom
mh/fix-CopyBits
Aug 31, 2026
Merged

kernel: Avoid an out-of-range shift in CopyBits#6534
fingolfin merged 1 commit into
masterfrom
mh/fix-CopyBits

Conversation

@fingolfin

@fingolfin fingolfin commented Aug 28, 2026

Copy link
Copy Markdown
Member

CopyBits and its helpers shift by amounts derived from <frombit>, <tobit> and the alignment difference between the two blocks. Several of those can reach BIPEB, where 1 << BIPEB and x >> BIPEB are undefined:

  • After a partial first destination word is filled, <frombit> can be left equal to BIPEB rather than wrapping to the next source word. MaskForCopyBits then evaluates (UInt)1 << BIPEB.
  • In the main loop <frombit> equal to 0 makes the second half of the word assembly shift by BIPEB - 0.
  • CopyInWord shifts by <shift>. The callers do keep it in range, but only indirectly: in the tobit + BIPEB - frombit case it takes the branch condition frombit + tailbits > BIPEB to show frombit > tobit.

Normalise <frombit> to the start of the next word, make MaskForCopyBits total so an out-of-range bound yields an empty mask, special-case the aligned main loop, and bound the shift in CopyInWord directly.

An exhaustive sweep over frombit, tobit in [0,BIPEB) and nbits in [1,5*BIPEB] hits the undefined shift in 1953 of 1310720 cases, and gives identical results before and after: in every one of those cases the mask being computed is dead, because no whole words remain to copy. So this changes no observable behaviour on current compilers.

The guards also do a second job. They are what lets a static analyzer establish the bounds: without them clang's core.BitwiseShift reports "Left shift overflows the capacity of 'UInt'" for paths reaching here from blister.c and vecgf2.c. Please keep them even where a local reading suggests the branch is dead.

Co-authored-by: Claude Opus 5 noreply@anthropic.com

@fingolfin fingolfin added topic: kernel release notes: not needed PRs introducing changes that are wholly irrelevant to the release notes labels Aug 28, 2026
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.97%. Comparing base (989cc35) to head (77a2746).

Files with missing lines Patch % Lines
src/bits_intern.h 66.66% 5 Missing and 4 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #6534      +/-   ##
==========================================
- Coverage   78.98%   78.97%   -0.01%     
==========================================
  Files         684      684              
  Lines      294205   294226      +21     
  Branches     8647     8678      +31     
==========================================
+ Hits       232370   232377       +7     
- Misses      60028    60034       +6     
- Partials     1807     1815       +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

CopyBits and its helpers shift by amounts derived from <frombit>,
<tobit> and the alignment difference between the two blocks. Several of
those can reach BIPEB, where `1 << BIPEB` and `x >> BIPEB` are undefined:

 * After a partial first destination word is filled, <frombit> can be
   left equal to BIPEB rather than wrapping to the next source word.
   MaskForCopyBits then evaluates `(UInt)1 << BIPEB`.
 * In the main loop <frombit> equal to 0 makes the second half of the
   word assembly shift by `BIPEB - 0`.
 * CopyInWord shifts by <shift>. The callers do keep it in range, but
   only indirectly: in the `tobit + BIPEB - frombit` case it takes the
   branch condition `frombit + tailbits > BIPEB` to show frombit > tobit.

Normalise <frombit> to the start of the next word, make MaskForCopyBits
total so an out-of-range bound yields an empty mask, special-case the
aligned main loop, and bound the shift in CopyInWord directly.

An exhaustive sweep over frombit, tobit in [0,BIPEB) and nbits in
[1,5*BIPEB] hits the undefined shift in 1953 of 1310720 cases, and gives
identical results before and after: in every one of those cases the mask
being computed is dead, because no whole words remain to copy. So this
changes no observable behaviour on current compilers.

The guards also do a second job. They are what lets a static analyzer
establish the bounds: without them clang's core.BitwiseShift reports
"Left shift overflows the capacity of 'UInt'" for paths reaching here
from blister.c and vecgf2.c. Please keep them even where a local reading
suggests the branch is dead.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

@lgoettgens lgoettgens left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand all the nitty bitty details, but looks sensible overall

@fingolfin
fingolfin merged commit 80990b8 into master Aug 31, 2026
33 checks passed
@fingolfin
fingolfin deleted the mh/fix-CopyBits branch August 31, 2026 13:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release notes: not needed PRs introducing changes that are wholly irrelevant to the release notes topic: kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants