immunant / c2rust

Migrate C code to Rust
https://c2rust.com/
Other
3.81k stars 220 forks source link

analyze: "unlower_map has no origin" on *mut -> *const cast #986

Open spernsteiner opened 1 year ago

spernsteiner commented 1 year ago

All four variants produce the same error:

unsafe fn read(p: *const u8) -> u8 {
    *p
}

unsafe fn cast_ptr_implicit(p: *mut u8) {
    *p = 1;
    read(p);
}

unsafe fn cast_ptr_explicit(p: *mut u8) {
    *p = 1;
    read(p as *const u8);
}

struct S {
    p: *mut u8,
}

unsafe fn cast_field_implicit(s: *mut S) {
    *(*s).p = 1;
    read((*s).p);
}

unsafe fn cast_field_explicit(s: *mut S) {
    *(*s).p = 1;
    read((*s).p as *const u8);
}

Relevant MIR (from cast_ptr_implicit):

    bb0[4]: _4 = _1
    bb0[5]: _3 = move _4 as *const u8 (Pointer(MutToConstPointer))
      []: StoreIntoLocal, c2rust-analyze/tests/filecheck/unlower_cast_ptr.rs:9:10: 9:11 (#0)
      [Rvalue]: Expr, c2rust-analyze/tests/filecheck/unlower_cast_ptr.rs:9:10: 9:11 (#0)

Here, we copy the argument p/_1 into the temporary _4, then cast the result and store it in _3, which is later passed as the argument to read. We wrongly identify the rvalue of bb0[5], move _4 as *const u8, as the Expr (the p in read(p)), but really the _1 in bb0[4] is the Expr. (This is easier to see in the struct field examples, where the expr (*s).p becomes the MIR rvalue ((*_1).0: *mut u8).)

The MIR looks the same for both the implicit coercion and explicit cast versions. For the implicit version in particular, we may need a new MirOriginDesc variant to describe exactly how bb0[4] and bb0[5] relate to the expression p.

spernsteiner commented 1 year ago

I was going to open a separate issue for this case, but I think it has the same root cause as the examples above:

struct S {
    p: *mut u8,
}

unsafe fn foo(s: *const S, cond: bool) -> *const u8 {
    if cond {
        (*s).p.offset(1)
    } else {
        (*s).p
    }
}

The MIR where the error occurs looks very similar to the bb0[4]-bb0[5] pattern above.