Skip to content

Incorrect size for long double when using old rust_target #1529

Closed
@Coder-256

Description

Long story short, long doubles 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 doubles 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 with rust-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

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

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions