-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix BitArray to clear dangling high bits in LeftShift and deserialization #123296
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
Conversation
Co-authored-by: stephentoub <[email protected]>
src/libraries/System.Collections/tests/BitArray/BitArray_OperatorsTests.cs
Show resolved
Hide resolved
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.
Pull request overview
This pull request fixes a bug in BitArray.LeftShift where unused high-order bits in the most significant storage element were not being cleared after the shift operation, causing PopCount() to return incorrect values when bits were shifted out of range.
Changes:
- Added a call to
ClearHighExtraBits()in theLeftShiftmethod to clear dangling high bits after shifting - Added regression test
LeftShift_PopCountReturnsCorrectCountto verify the fix
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/libraries/System.Private.CoreLib/src/System/Collections/BitArray.cs | Added ClearHighExtraBits() call after left shift operation to clear unused high bits |
| src/libraries/System.Collections/tests/BitArray/BitArray_OperatorsTests.cs | Added regression test that verifies PopCount returns 0 after shifting the last bit out of range |
Co-authored-by: stephentoub <[email protected]>
Co-authored-by: stephentoub <[email protected]>
tannergooding
left a comment
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.
Are there any other missed edge cases where the upper bits might be set?
We exposed Span<byte> CollectionsMarshal.AsBytes(BitArray array) as well, in which case a user can set the "high extra bits" themselves. Are we leaving that as undefined behavior or is it something we want to explicitly handle for APIs like PopCount?
We've been leaving that as undefined behavior, and methods that change them need to clear them to leave those bits 0s. The alternative or addition if we want is every method that might care needs to clear those bits first.
@copilot, have we missed any other cases anywhere in BitArray where these upper unused bits should be cleared and aren't? |
Co-authored-by: stephentoub <[email protected]>
I audited all methods in BitArray and found one additional case: the deserialization constructor was copying data from the serialized int[] array without clearing the high bits. If the serialized data came from an older version or was malformed, the high extra bits could be set. I've added The other operations are safe:
|
|
/ba-g failures are unrelated |
Description
BitArray.LeftShiftwas not clearing unused bits in the most significant storage element after shifting. This causedPopCountto return incorrect values when bits were shifted out of range.Additionally, the deserialization constructor was not clearing unused high bits after copying data from the serialized int[] array, which could leave garbage bits if the serialized data came from older versions or was malformed.
Changes
ClearHighExtraBits()after shift operation completes inLeftShiftClearHighExtraBits()in the deserialization constructor to ensure the invariant is maintained when deserializing dataLeftShiftandRightShiftoperations validatingPopCountandHasAnySetwith various bit patterns and shift countsOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.