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

coercion of mutable pointers' child type causes a soundness issue #20738

Closed
Fri3dNstuff opened this issue Jul 22, 2024 · 3 comments
Closed

coercion of mutable pointers' child type causes a soundness issue #20738

Fri3dNstuff opened this issue Jul 22, 2024 · 3 comments
Labels
bug Observed behavior contradicts documented or intended behavior miscompilation The compiler reports success but produces semantically incorrect code.
Milestone

Comments

@Fri3dNstuff
Copy link
Contributor

Fri3dNstuff commented Jul 22, 2024

Zig Version

0.14.0-dev.367+a57479afc

Steps to Reproduce and Observed Behavior

Attempt to compile the following code:

pub fn main() void {
    const n: u8 align(2) = 42;
    const m: u8 align(1) = 0;

    var t: *align(2) const u8 = &n;
    var u: *align(1) const u8 = &m;
    _ = .{ &t, &u };

    const pt: **align(2) const u8 = &t;
    const pu: **align(1) const u8 = pt; // this coercion is bad!

    pu.* = u;
    // oh no! we've stored an align(1) value in a
    // location that expects to hold an align(2) value
}

The code compiles without errors.

Expected Behavior

The code should have triggered a compilation error.

A mutable pointer to (one / many / slice / C) a type T can coerce into a mutable pointer of type U without soundness problems if-and-only-if:

  • T and U are in-memory coercible, and
  • every valid bit-pattern of the one is a valid bit-pattern of the other.

These are, I believe, the most liberal coercion rules that do not result in soundness problems - I will leave the discussion about whether Zig should be this liberal with its coercion rules to another issue.

@Fri3dNstuff Fri3dNstuff added the bug Observed behavior contradicts documented or intended behavior label Jul 22, 2024
@rohlem
Copy link
Contributor

rohlem commented Jul 22, 2024

It's already noted in the issue text, but important to note that coercion of pointers-to-const *const *align(2) T -> *const *align(1) T can be allowed.
We only need to prevent it when the target pointer-to-mutable broadens the set of valid pointee values.

@andrewrk andrewrk added the miscompilation The compiler reports success but produces semantically incorrect code. label Jul 26, 2024
@andrewrk andrewrk added this to the 0.14.0 milestone Jul 26, 2024
@Fri3dNstuff
Copy link
Contributor Author

I just stumbled upon another manifestation of the issue, now with sentinel-terminated types:

test {
    var x: [*:0]const u8 = "abc";
    const good_ref: *[*:0]const u8 = &x;
    const evil_ref: *[*]const u8 = good_ref; // this coercion is bad!

    evil_ref.* = @as([*]const u8, &.{ 'a', 'b', 'c' });
    // oops! `x` is now storing a
    // non-sentinel-terminated pointer!
}

this is the classic unsoundness of covariance of mutable references. to lay out the sound conversions:

  • a mutable reference is invariant on its child type
  • a const reference is covariant on its child type
  • a write-only reference (though such type does not exist in Zig...) is contravariant on its child type

in Zig we also care that the coercion is possible in-memory (i.e. we just reinterpret bits), and that mutable references to T can be cast into mutable references to U and back if T and U are in-memory coercible into each other

@Fri3dNstuff
Copy link
Contributor Author

fixed by #22243

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior miscompilation The compiler reports success but produces semantically incorrect code.
Projects
None yet
Development

No branches or pull requests

3 participants