Incorrect size for long double when using old rust_target #1529
Description
Long story short, long double
s are mistreated, leading to a SEVERE security/stability issue. I don't think I am overreacting. On some platforms (for example, my own computer), long double
s are 16 bytes! Not 8! This can cause a stack/heap overflow and all sorts of issues. This is enough to trigger the bug!:
foo.h
:
void mul_assign_long_double(long double *input, double multiplier);
foo.c
:
void mul_assign_long_double(long double *input, double multiplier) {
*input *= multiplier;
}
test.rs
:
#[derive(Debug)]
#[repr(C)]
struct StackOverflowStruct {
a: f64, // Target of our C function
b: u64,
c: u64
}
unsafe fn to_bytes(input: &StackOverflowStruct) -> std::string::String {
let x = std::mem::transmute_copy::<StackOverflowStruct, [u64; 3]>(input);
format!("[{:#X},{:#X},{:#X}]", x[0], x[1], x[2])
}
fn stack_overflow() {
unsafe {
let mut test_struct = StackOverflowStruct {
a: std::mem::transmute::<u64, f64>(9367522409571165184), // constructed
b: 16391, // constructed
c: 12345 // random
};
println!("BEFORE SO: {:?}", test_struct);
println!("BEFORE SO (bytes): {}", to_bytes(&test_struct));
mul_assign_long_double(&mut test_struct.a as *mut _, 89237843.0); // random
println!("AFTER SO: {:?}", test_struct);
println!("AFTER SO (bytes): {}", to_bytes(&test_struct));
}
}
Output:
BEFORE SO: StackOverflowStruct { a: -0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004815640556295841, b: 16391, c: 12345 }
BEFORE SO (bytes): [0x8200200010002400,0x4007,0x3039]
AFTER SO: StackOverflowStruct { a: -0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014459992918754277, b: 16417, c: 12345 }
AFTER SO (bytes): [0xACDE2996DFDED52B,0x4021,0x3039]
Notice that b
has been affected! (the middle value of the array). It changed from 16391
to 16417
. This is because although we only pass a pointer to a
, it is interpreted as a 16-byte pointer, but a
only has enough space for 8 bytes so it overwrites into 2 bytes of b
! Sorry for all the exclamation points, but this is a serious issue!
This is a very very bad thing.
In addition to fixing this issue, this must be treated as a severe vulnerability. Personally I would:
- "Yank" all unfixed versions
- Notify users ASAP, anybody using
rust-bindgen
with mission-critical code should stop anything built withrust-bindgen
until they get a fixed version and update their code - Provide a fixed version, and possibly backport the fix to old versions
It would be very easy to import a large C header/library, see a f64
where it would make sense, and assume everything is OK. And all tests might pass. But then when you run your code in production, the stack is corrupted! Not only could this happen randomly with well-behaved code, but it could also be exploited to overwrite up to 2 bytes (long double
is usually only 10 important bytes; 10 - 8 = 2) on the stack with attacker-controlled values. Plus it would be very hard to debug. That is why I am treating this so seriously! In issues like #1338 and #550, it has been brushed off and treated as a small bug because there is no easy fix, but currently, it is not just a bug, it is actually unsafe/undefined behavior, and AFAIK there is no warning about it at all.
Since Rust does not have a f128
, there is no clear replacement, but that debate can be held later (u128
? [u64; 2]
?). Right now, I think the main concern should be at the very least notifying people using rust-bindgen
about this issue.
Activity