Skip to content

fix(ssz): ignore BitVector padding bits#329

Open
lodekeeper-z wants to merge 2 commits intoChainSafe:feat/beacon-nodefrom
lodekeeper-z:fix/bitvector-padding-bits
Open

fix(ssz): ignore BitVector padding bits#329
lodekeeper-z wants to merge 2 commits intoChainSafe:feat/beacon-nodefrom
lodekeeper-z:fix/bitvector-padding-bits

Conversation

@lodekeeper-z
Copy link
Copy Markdown
Contributor

Summary

  • Mask non-byte-aligned BitVector padding bits in true-bit helper paths
  • Prevent padding bits from being returned as logical indexes in getTrueBitIndexes/getSingleTrueBit/intersectValues
  • Add regression coverage for a 4-bit vector with high padding bits set

Production context

  • Fixes a nogroup crash in Electra/Fulu attestation processing at process_attestation_phase0.zig:130 when committee_bits padding bits were treated as committee indexes.
  • Deployed binary sha on nogroup-rs2000-0: 4a676de5ed8405a6bbaeac97e86eb0bdb5ddf2d5c73018c330f77ef222f9e94f

Test plan

  • /snap/bin/zig build test:ssz -Dssz.filters="BitVectorType helpers ignore padding bits" --summary all
  • /snap/bin/zig build test:ssz --summary all
  • /snap/bin/zig build -Doptimize=ReleaseSafe --summary all
  • Remote nogroup focused SSZ test and ReleaseSafe build passed

@lodekeeper-z lodekeeper-z requested a review from a team as a code owner April 28, 2026 08:31
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical issue where padding bits in BitVectors were incorrectly interpreted as valid data, leading to crashes during attestation processing. By implementing a masking mechanism for non-byte-aligned bits, the changes ensure that only relevant bits are processed, improving the robustness of SSZ type handling.

Highlights

  • Padding Bit Masking: Introduced a maskedByte helper method to ensure that padding bits in BitVectors are ignored during bitwise operations.
  • Helper Function Updates: Updated getTrueBitIndexes, getSingleTrueBit, and intersectValues to utilize the new masking logic, preventing incorrect logical index identification.
  • Regression Testing: Added a new test case to verify that BitVector helper functions correctly handle vectors with high-order padding bits set.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a maskedByte helper function to BitVector in src/ssz/type/bit_vector.zig to ensure that padding bits are ignored during bitwise operations. This helper is applied across getTrueBitIndexes, getSingleTrueBit, and intersectValues, and a corresponding test case has been added. The reviewer suggested refactoring the maskedByte implementation to comply with the repository's style guide, specifically regarding the decomposition of compound conditions and the inclusion of pre- and post-condition assertions.

Comment thread src/ssz/type/bit_vector.zig Outdated
Comment on lines +43 to +50
fn maskedByte(self: *const @This(), i_byte: usize) u8 {
const remainder_bits = length % 8;
if (remainder_bits == 0 or i_byte + 1 < byte_len) {
return self.data[i_byte];
}
const tail_mask: u8 = (@as(u8, 1) << @intCast(remainder_bits)) - 1;
return self.data[i_byte] & tail_mask;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The maskedByte function should be refactored to adhere to the repository style guide:

  1. Control Flow (Rule 133): Split the compound condition into nested if/else branches. This improves readability and ensures that the logic for handling the last byte is explicitly separated from the common case.
  2. Assertions (Rule 51): Add a precondition assertion to verify that i_byte is within bounds. This helps catch programmer errors early and documents the function's contract.
  3. Pair Assertions (Rule 58): Add an assertion to verify that the resulting byte has no bits set beyond the logical length. This provides defense-in-depth and documents the expected state of the data.
  4. Assertion Style (Rule 68): Use single-line if for assertions of implications where appropriate.
        fn maskedByte(self: *const @This(), i_byte: usize) u8 {
            std.debug.assert(i_byte < byte_len);

            const remainder_bits = length % 8;
            if (remainder_bits == 0) {
                return self.data[i_byte];
            } else {
                if (i_byte + 1 < byte_len) {
                    return self.data[i_byte];
                }
            }

            const tail_mask: u8 = (@as(u8, 1) << @intCast(remainder_bits)) - 1;
            const byte = self.data[i_byte] & tail_mask;

            // Pair assertion: ensure no bits are set beyond the logical length.
            if (remainder_bits > 0) std.debug.assert(@clz(byte) >= 8 - remainder_bits);

            return byte;
        }
References
  1. Split compound conditions into simple conditions using nested if/else branches. (link)
  2. Assert all function arguments and return values, pre/postconditions and invariants. (link)

@wemeetagain
Copy link
Copy Markdown
Member

I think maskedByte should only be needed for the final byte

@lodekeeper-z
Copy link
Copy Markdown
Contributor Author

lodekeeper-z commented May 7, 2026

Updated in fe4768a: maskedFinalByte() is now only used for the final byte when the BitVector length is not byte-aligned; full bytes read self.data[i_byte] directly.

Verification:

  • zig build test:ssz -Dssz.filters='BitVectorType helpers ignore padding bits'
  • zig build test:ssz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants