I have a small C code which demonstrate Runtime Stack functionality by modifying data at a stack address.
#include <stdio.h>
int * fun() {
int a = 10;
return &a;
}
int * fun2() {
int b = 20;
return &b;
}
int main () {
int *a = fun();
int *b = fun2();
printf("a:%d b:%d\n", *a, *b);
return 0;
}
Output of this is : a:20 b:20 which shows 'b' in 'fun2' used the same stack address as 'a' in 'fun'.
i want to test this in Rust too. What would be the simplest way to do this?
I tried Borrowing but compiler says "No value to borrow".
CodePudding user response:
If you want to have the same undefined behavior as in C, you can do (luckily, this requires unsafe
):
fn fun() -> *const i32 {
let a = 10;
&a
}
fn fun2() -> *const i32 {
let b = 20;
&b
}
fn main() {
let a = fun();
let b = fun2();
// SAFETY: Don't do this. This is pure Undefined Behavior.
unsafe {
println!("a: {} b: {}", *a, *b);
}
}
And this is UB exactly as with the C version. It prints a: 21885 b: 21885
on the playground on debug mode, and a: -482758656 b: 32767
on release mode. Yours will be different.
If you also want the same result as the C version, you can use printf()
. Be careful with the code you use, changes can cause it to no longer "work" as it will use a different stack layout. Of course, this is very fragile:
use std::ffi::c_char;
fn fun() -> *const i32 {
let a = 10;
&a
}
fn fun2() -> *const i32 {
let a = 20;
&a
}
extern "C" {
fn printf(format: *const c_char, ...);
}
fn main() {
let a = fun();
let b = fun2();
unsafe {
printf(b"a: %d b: %d\n\0" as *const u8 as *const c_char, *a, *b);
}
}
It prints a: 20 b: 20
on debug mode, and a: 1 b: 1
on release (and yours may be different).
CodePudding user response:
What is the RUST equivalent of following C code?
Goal is questionable as C code is bad.
int *a = fun();
is undefined behavior (UB) as code attempts to save an invalid pointer. Copying invalid pointers is UB. The later attempted reference of *a
in printf("a:%d b:%d\n", *a, *b);
is also UB.
int * fun() {
int a = 10;
return &a; // &a invalid when function completes.
}
CodePudding user response:
a
and b
have automatic storage and are destroyed once the function returns. Once a function returns, it's stack frame is popped (destroyed). Returning the address of (and using) the local variables invokes Undefined Behaviour as per the following clause of the C11 standard:
6.2.4 Storage durations of objects:
2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address, and retains its last-stored value throughout its lifetime. If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
CodePudding user response:
literally the same:
#![no_main]
extern "C" {
fn printf(format: *const u8, ...); // u8 or i8 doesn't matter in this case
}
#[no_mangle]
extern fn fun() -> *const i32 {
let a = 10;
&a
}
#[no_mangle]
extern fn fun2() -> *const i32 {
let a = 20;
&a
}
#[no_mangle]
unsafe extern main() -> i32 {
let a = fun();
let b = fun2();
printf("a:%d b:%d\n\0".as_ptr(), *a, *b);
0
}