Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

try_transmute_mut, TryFromBytes::*mut* soundness issue #2226

Open
4 of 6 tasks
zeling opened this issue Jan 10, 2025 · 3 comments
Open
4 of 6 tasks

try_transmute_mut, TryFromBytes::*mut* soundness issue #2226

zeling opened this issue Jan 10, 2025 · 3 comments

Comments

@zeling
Copy link
Member

zeling commented Jan 10, 2025

Progress

  • Add Src: FromBytes bound to try_transmute_mut!
  • Release new crate version (0.8.16)
  • Add Self: IntoBytes bound to TryFromBytes::*mut*
    • For why this is required, consider that a MaybeUninit<u8> is TryFromBytes, but permits writing uninitialized bytes that would invalidated the shadowed src reference.
  • Release new crate version (0.8.18)
  • Yank old affected versions
  • Do deeper surgery to make try_cast_or_pme sound and TryFromBytes::*mut* sound

Original text

Using zerocopy 0.8.13:

use zerocopy::{TryFromBytes, IntoBytes, KnownLayout, Immutable, try_transmute_mut};

#[derive(TryFromBytes, IntoBytes, KnownLayout, Immutable)]
struct T {
    f: bool,
}

fn main() {
    let mut t = T { f: false };
    let slice: &mut [u8; 1] = try_transmute_mut!(&mut t).unwrap();

    slice[0] = u8::MAX;
    println!("f: {}", t.f);
}

cargo +nightly miri run caught an UB:

error: Undefined Behavior: constructing invalid value: encountered 0xff, but expected a boolean
    --> /home/zeling/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:2682:25
     |
2682 |         Display::fmt(if *self { "true" } else { "false" }, f)
     |                         ^^^^^ constructing invalid value: encountered 0xff, but expected a boolean
     |
     = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
     = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
     = note: BACKTRACE:
....
     |
13   |     println!("f: {}", t.f);
     |     ^^^^^^^^^^^^^^^^^^^^^^
     = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

try_transmute_mut may not be a safe API that can be exposed because it only does validation when creating the reference but the user is free to write whatever bit pattern to the created reference.

@jswrenn
Copy link
Collaborator

jswrenn commented Jan 11, 2025

Ack; thanks for the report. It looks like we're missing a FromBytes bound on the source. I'll have a fix up this evening.

jswrenn added a commit that referenced this issue Jan 12, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Fixes #2226
jswrenn added a commit that referenced this issue Jan 12, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Fixes #2226
jswrenn added a commit that referenced this issue Jan 12, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Fixes #2226
jswrenn added a commit that referenced this issue Jan 27, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Makes progress on #2226
jswrenn added a commit that referenced this issue Feb 4, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Makes progress on #2226
github-merge-queue bot pushed a commit that referenced this issue Feb 4, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Makes progress on #2226
@joshlf
Copy link
Member

joshlf commented Feb 4, 2025

#2229, which fixes this issue at a surface level (it's sound, but doesn't fix the underlying internals that allowed this to slip through) is published in 0.8.16.

@jswrenn jswrenn changed the title try_transmute_mut soundness issue try_transmute_mut, TryTransmuteFrom::*mut* soundness issue Feb 11, 2025
@jswrenn
Copy link
Collaborator

jswrenn commented Feb 11, 2025

The underlying issue that led to us not enforce a FromBytes bound on try_transmute_mut also led us to omit a Self: IntoBytes bound on TryFromBytes's mut methods. Our intention is to release a surface-level fix in 0.8, and then overhaul Ptr in 0.9 to make such mistakes less likely (see #1866).

@jswrenn jswrenn changed the title try_transmute_mut, TryTransmuteFrom::*mut* soundness issue try_transmute_mut, TryFromBytes::*mut* soundness issue Feb 11, 2025
jswrenn added a commit that referenced this issue Feb 11, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
joshlf pushed a commit that referenced this issue Feb 14, 2025
Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Makes progress on #2226

gherrit-pr-id: I425e7d5103cb3b2a9e7107bf9df0743dca2e08cb
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
github-merge-queue bot pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
joshlf pushed a commit that referenced this issue Feb 14, 2025
Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c
github-merge-queue bot pushed a commit that referenced this issue Feb 14, 2025
…ryFromBytes::try_mut*` (#2343)

* Enforce `Src: FromBytes` in `try_transmute_mut!` (#2229)

Ensures that the source reference remains valid after the
transmuted (and possibly mutated)  destination is dropped.

Makes progress on #2226

gherrit-pr-id: I425e7d5103cb3b2a9e7107bf9df0743dca2e08cb

* Add `Self: IntoBytes` bound to `TryFromBytes::try_mut*`

Consider that `MaybeUninit<u8>` is `TryFromBytes`. If a `&mut [u8]`
is cast into a `&mut MaybeUninit<u8>`, then uninit bytes are written,
the shadowed `&mut [u8]`'s referent will no longer be valid.

Makes progress towards #2226 and #1866.

gherrit-pr-id: Ib233c4d0643e0690c53a37a08d9845e5fe43249c

---------

Co-authored-by: Jack Wrenn <jswrenn@amazon.com>
Co-authored-by: Jack Wrenn <jack@wrenn.fyi>
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants