Why does Rust allow calling functions via null pointers?

前端 未结 5 937
臣服心动
臣服心动 2021-02-05 00:21

I was experimenting with function pointer magic in Rust and ended up with a code snippet which I have absolutely no explanation for why it compiles and even more, why it runs.

5条回答
  •  隐瞒了意图╮
    2021-02-05 00:50

    This is "working" because fn() {foo} and the first closure are zero-sized types. Extended answer:

    If this program ends up executed in Miri (Undefined behaviour checker), it ends up failing because NULL pointer is dereferenced. NULL pointer cannot ever be dereferenced, even for zero-sized types. However, undefined behaviour can do anything, so compiler makes no promises about the behavior, and this means it can break in the future release of Rust.

    error: Undefined Behavior: memory access failed: 0x0 is not a valid pointer
      --> src/main.rs:7:28
       |
    7  |     let closure = unsafe { &mut *closure_ptr };
       |                            ^^^^^^^^^^^^^^^^^ memory access failed: 0x0 is not a valid pointer
       |
       = 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: inside `caller::` at src/main.rs:7:28
    note: inside `create::` at src/main.rs:13:5
      --> src/main.rs:13:5
       |
    13 |     func_ptr();
       |     ^^^^^^^^^^
    note: inside `main` at src/main.rs:17:5
      --> src/main.rs:17:5
       |
    17 |     create(foo);
       |     ^^^^^^^^^^^
    

    This issue can be easily fixed by writing let closure_ptr = 1 as *mut F;, then it will only fail on line 22 with the second closure that will segfault.

    error: Undefined Behavior: inbounds test failed: 0x1 is not a valid pointer
      --> src/main.rs:7:28
       |
    7  |     let closure = unsafe { &mut *closure_ptr };
       |                            ^^^^^^^^^^^^^^^^^ inbounds test failed: 0x1 is not a valid pointer
       |
       = 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: inside `caller::<[closure@src/main.rs:22:12: 22:55 val:&i32]>` at src/main.rs:7:28
    note: inside `create::<[closure@src/main.rs:22:12: 22:55 val:&i32]>` at src/main.rs:13:5
      --> src/main.rs:13:5
       |
    13 |     func_ptr();
       |     ^^^^^^^^^^
    note: inside `main` at src/main.rs:22:5
      --> src/main.rs:22:5
       |
    22 |     create(|| println!("This will seg fault: {}", val));
       |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    

    Why it didn't complain about foo or || println!("Okay...")? Well, because they don't store any data. When referring to a function, you don't get a function pointer but rather a zero-sized type representing that specific function - this helps with monomorphization, as each function is distinct. A structure not storing any data can be created from aligned dangling pointer.

    However, if you explicitly say the function is a function pointer by saying create::(foo) then the program will stop working.

提交回复
热议问题