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

Linux: ucontext_t definition is only correct for glibc 2.28 and above #1410

Closed
acfoltzer opened this issue Jun 26, 2019 · 9 comments
Closed

Comments

@acfoltzer
Copy link
Contributor

This commit introduced a shadow stack __ssp field to the definition of ucontext_t: 4497a78

However, that field is only present in glibc 2.28 and above, so this struct doesn't work correctly on distros with earlier versions, including Ubuntu 18.04 where I encountered memory corruption after updating from 0.2.47 to 0.2.58.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

How is code compiled for glibc < 2.28 ABI compatible with >= 2.28 ?

@acfoltzer
Copy link
Contributor Author

Sorry, I must not be understanding your question. I'm on a < 2.28 distro, compiling and running against < 2.28; I currently don't use any >= 2.28 distros.

acfoltzer added a commit to bytecodealliance/lucet that referenced this issue Jun 26, 2019
Includes some other fixes:

- pinned libc crate to 0.2.54 until the resolution of rust-lang/libc#1410
- updated C API types for the removal of tags from trapcodes
@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

If you compile a binary against glibc < 2.28 headers, ucontext_t has a smaller size than if one uses glibc >= 2.28. AFAIK, you can compile against glibc < 2.28 and then use that binary in a system where glibc >= 2.28 is dynamically linked. I was wondering how does this work here?

E.g., getcontext on glibc >= 2.28 might try to write to the extra field of ucontext_t, but if your library used older headers, it wouldn't have the field, so that would be a write out-of-bounds.

Maybe there are new different symbols for newer getcontext, so that if you compile your binary against an older libc, and dynamically link against a new version, the older symbols get picked, and these do not write to the extra field ? If so, then we could fix this by using link_name to make these APIs link to their older symbols.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

Relevant commit: bminor/glibc@25123a1

So apparently that's allowed, see: bminor/glibc@25123a1 glibc checks whether the ucontext_t being used has the new field, and if not, does something else.

Older glibc's can, AFAICT, just ignore the field. If you write:

let mut ucontext = MaybeUninit::<ucontext_t>::zeroed().into_inner();
getcontext(&mut ucontext);

that should work fine, because getcontext in glibc < 2.28 doesn't know about the extra field, and it doesn't read or write to it, it just completely ignores it (note that the ucontext_t is passed via memory here, so call ABI wise there are no issues either - AFAICT this is the case for all libc APIs).

Layout wise, the new field does not lower the alignment requirements, so that shouldn't be an issue either.

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

Do you have a minimal working example showing the issue ?

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

(This issue might be related: nix-rust/nix#1092)

@acfoltzer
Copy link
Contributor Author

So, this is coming up in a case where a Rust program is writing a libc::ucontext_t to a pointer to a ucontext_t provided by C. Since the size of the struct is smaller on the C side, when we write to it using the Rust type we write off the end into memory we shouldn't have access to.

Here is a quick and dirty example using a stack-allocated ucontext_t, which triggers the stack-smashing detection: https://github.com/acfoltzer/ucontext-bug

On my system, make results in the following:

acfoltzer@stribog:~/src/ucontext-bug % make
cargo build
   Compiling ucontext-bug v0.1.0 (/home/acfoltzer/src/ucontext-bug)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
cc main.c target/debug/libucontext_bug.so -o main
./main
C passing pointer to a struct of size 936
Rust writing struct of size 968
*** stack smashing detected ***: <unknown> terminated
Makefile:3: recipe for target 'run' failed
make: *** [run] Aborted (core dumped)

@gnzlbg
Copy link
Contributor

gnzlbg commented Jun 26, 2019

Gotcha. AFAICT the easiest way to fix this is to revert that change, skipping ucontext in libc-test/build.rs for linux, at least until less users are using glibc < 2.28.

acfoltzer added a commit to acfoltzer/libc that referenced this issue Jun 26, 2019
Per discussion in rust-lang#1410, this is necessary to avoid struct size mismatches between Rust and C on
systems with glibc < 2.28.
bors added a commit that referenced this issue Jun 27, 2019
Remove new field from ucontext_t for compatibility with earlier glibc versions

Per discussion in #1410 with @gnzlbg, this is necessary to avoid struct size mismatches between Rust and C on systems with glibc < 2.28.
bors added a commit that referenced this issue Jun 27, 2019
Remove new field from ucontext_t for compatibility with earlier glibc versions

Per discussion in #1410 with @gnzlbg, this is necessary to avoid struct size mismatches between Rust and C on systems with glibc < 2.28.
bors added a commit that referenced this issue Jun 27, 2019
Remove new field from ucontext_t for compatibility with earlier glibc versions

Per discussion in #1410 with @gnzlbg, this is necessary to avoid struct size mismatches between Rust and C on systems with glibc < 2.28.
@acfoltzer
Copy link
Contributor Author

Closed by #1411. Thanks!

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

No branches or pull requests

2 participants