Skip to content
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

Fix verifier shift instruction overflows imm value #212

Merged

Conversation

disconnect3d
Copy link

@disconnect3d disconnect3d commented Aug 10, 2021

Before this commit, the verifier didn't properly check against shift overflows in instructions that operate on 32-bit registers. Those instructions are: lsh32_imm, rsh32_imm and arsh32_imm.

For those instructions, the verifier used the same check for shift overflow as for instructions that operate on 64-bit registers: it called the check_imm_shift(&insn, insn_ptr) function, which was implemented in the following way:

/// Check that the imm is a valid shift operand
fn check_imm_shift(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
    if insn.imm < 0 || insn.imm as u64 >= 64 {
        return Err(VerifierError::ShiftWithOverflow(adj_insn_ptr(insn_ptr)));
    }
    Ok(())
}

Note that here, we ensure the argument is not below 0 (insn.imm < 0) and that it is not greater or equal than 64 (insn.imm as u64 >= 64).

This check is incorrect for 32-bit versions of shift instructions which won't be marked with shift overflow for arguments in range [32, 63].

This can be problematic as shifting by more bits than the register size can lead to UB.

Please note that this bug can't be triggered with current Solana examples (https://github.com/solana-labs/example-helloworld/tree/52b0f1afb90ccb1af28c1c25030366e27a1e9834). This is because:

  1. The Rust compiler won't allow for such shift overflow
  2. The C compiler warns about it and the C example is compiled with the -Werror flag which makes the overflowing shift an error

However, this of course doesn't prevent the bytecode itself from having such invalid and overflowing shift value and so this commit fixes it making the verifier to check against those cases.

Before this commit, the verifier didn't properly check against shift
overflows in instructions that operate on 32-bit. Those instructions
are: `lsh32_imm`, `rsh32_imm` and `arsh32_imm`.

For those instructions, the verifier used the same check for shift
overflow: it called `check_imm_shift(&insn, insn_ptr)`.

This function was implemented in the following way:
```rust
/// Check that the imm is a valid shift operand
fn check_imm_shift(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), VerifierError> {
    if insn.imm < 0 || insn.imm as u64 >= 64 {
        return Err(VerifierError::ShiftWithOverflow(adj_insn_ptr(insn_ptr)));
    }
    Ok(())
}
```

Note that here, we ensure the argument is not below 0 (`insn.imm < 0`)
and that it is not greater or equal than 64 (`insn.imm as u64 >= 64`).

This check is incorrect for 32-bit versions of shift instructions which
won't be marked with shift overflow for arguments in range [32, 63].

This can be problematic as shifting by more bits than the register size
can lead to UB.

Please note that this bug can't be triggered with current Solana
examples (https://github.com/solana-labs/example-helloworld/tree/52b0f1afb90ccb1af28c1c25030366e27a1e9834).
This is because:
1) The Rust compiler won't allow for such shift overflow
2) The C compiler warns about it and the C example is compiled with the `-Werror` flag which makes the overflowing shift an error

However, this of course doesn't prevent the bytecode itself from having
such invalid and overflowing shift value and so this commit fixes it.
@Lichtso
Copy link

Lichtso commented Aug 11, 2021

Good work!

Will have to make a config option (like this here), feature gate it and also check if none of the already deployed programs fail it.

@Lichtso Lichtso merged commit 64ff80c into solana-labs:main Aug 13, 2021
Lichtso added a commit that referenced this pull request Aug 13, 2021
Lichtso added a commit that referenced this pull request Aug 16, 2021
@dmakarov dmakarov mentioned this pull request Nov 15, 2021
dmakarov added a commit that referenced this pull request Nov 15, 2021
- Fix R_BPF_64_64 relocation handling (#225)
- Fixes the build of the CLI tool. (#224)
- Clearify error message MultipleTextSections => NotOneTextSection. (#223)
- Remove disabled infinite loop check (#219)
- Fix tests to pass on non-X86_64 architectures (#218)
- Fix Beta CI Tests (#216)
- Make disassembler output more readable (#215)
- Makes #212 configurable for feature gate. (#213)
- Fix verifier shift instruction overflows imm value (#212)
@msuiche
Copy link

msuiche commented Nov 15, 2021

@Lichtso in which scenario would you have verify_shift32_imm set to false though?

@Lichtso
Copy link

Lichtso commented Nov 15, 2021

@msuiche It is just a feature flag to coordinate the switch from one version to the next across the distributed network. So in the future it will always be true and can then be removed.

@msuiche
Copy link

msuiche commented Nov 15, 2021

Ah got it. Thanks for the explanation :) I didn't realize it had to be coordinated, I thought it was just for internal testing purposes.

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.

3 participants