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

Make EbpfError enum discriminant size explicit #361

Merged
merged 2 commits into from
Aug 10, 2022

Conversation

ndrewh
Copy link

@ndrewh ndrewh commented Jul 27, 2022

This PR is on behalf of Trail of Bits.

@VTCAKAVSMoACE let me know of some warnings when running my aarch64 PR (#358) under MSAN. I investigated these, and it turns out that emit_set_exception_kind has been writing some uninitialized bytes to the JIT region, even in the current X86 JIT.

Enum layouts are generally not well-defined, and currently the compiler is choosing to make the discriminant on EbpfError a u8, leaving bytes 1-7 as uninitialized stack memory:

100109fe1  48c785d8feffff00…  mov     qword [rbp-0x128 {var_130}], 0x0
100109fec  48c785d0feffff00…  mov     qword [rbp-0x130 {var_138}], 0x0
100109ff7  c685c8feffff0b     mov     byte [rbp-0x138 {var_140}], 0xb
100109ffe  488db5c8feffff     lea     rsi, [rbp-0x138 {var_140}]
10010a005  4889df             mov     rdi, rbx
10010a008  e823baffff         call    solana_rbpf::jit_x86::emit_set_exception_kind::h38f55395ff38d700

These bytes are then written into the jit in emit_set_exception_kind:

let err = Result::<u64, EbpfError<E>>::Err(err);
let err_kind = unsafe { *(&err as *const _ as *const u64).offset(1) };
...
emit_ins(jit, X86Instruction::store_immediate(OperandSize::S64, R10, X86IndirectAccess::Offset(8), err_kind as i64));

https://github.com/solana-labs/rbpf/blob/main/src/jit.rs#L855

This was getting caught in the aarch64 JIT under MSAN because there is branching based on that immediate value before it gets written into the JIT region.

This PR adds #[repr(u64)] to the enum, which makes the discriminant size explicit.

https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields

I do not believe this has any direct security consequences, since these uninitialized bytes are never read (everything that deals with EbpfError enums only pulls the single discriminant byte out of memory). At worst, if these bytes are controllable (they do not appear to be), this might partially bypass the constant sanitization mitigation implemented in #143.

The JIT also dangerously assumes a similar layout for std::result::Result, which probably isn't guaranteed by the compiler either (currently the discriminant on Result appears to be u64, which aligns with the JIT's assumptions in various places). This PR adds tests that will hopefully catch any changes in enum layout in the future.

It turns out that you can't try to ask for the size of
std::mem::Discriminant and expect the right answer, so these
tests mimic what the JIT is doing.
@Lichtso Lichtso merged commit 07d7a49 into solana-labs:main Aug 10, 2022
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