Rust's libc MAP_FIXED is incorrect
I tested this code
use std::ffi::c_void;
use std::ptr;
fn main() {
// Get the system's page size. Memory protection works on a page-by-page basis.
let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
println!("System page size: {} bytes", page_size);
// --- NEW: Define a fixed address for MAP_FIXED demonstration ---
// WARNING: MAP_FIXED is dangerous. It can overwrite existing memory mappings
// if the chosen address is already in use, leading to undefined behavior or crashes.
// For this example, we pick an arbitrary high address that is likely unused.
// On a 64-bit system, this will likely work. On 32-bit, it will fail.
let fixed_address = 0x100000000 as *mut c_void;
println!("\nAttempting to map memory at fixed address: {:?}", fixed_address);
// Allocate one page of memory using mmap with MAP_FIXED.
// This tells the OS that we require the mapping at the specific address.
let memory = unsafe {
libc::mmap(
fixed_address, // Use the exact address we specified
page_size, // Allocate one page
libc::PROT_READ | libc::PROT_WRITE, // Initial protection: Read + Write
// Add the MAP_FIXED flag
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | libc::MAP_FIXED,
-1, // No file descriptor
0, // No offset
)
};
if memory == libc::MAP_FAILED {
panic!("mmap failed");
}
// With MAP_FIXED, the returned address should be the one we requested.
if memory != fixed_address {
// This case is unlikely with MAP_FIXED as it should either succeed at the address or fail.
// However, it's good practice to be thorough.
panic!(
"mmap with MAP_FIXED returned a different address! Expected {:?}, got {:?}",
fixed_address, memory
);
}
println!("Successfully allocated one page at address: {:?}", memory);
// Cast the void pointer to a mutable u8 pointer to work with it.
let mem_ptr = memory as *mut u8;
// --- 1. Demonstrate WRITE access ---
// The memory is currently writable, so let's write a value.
unsafe {
println!("\n--- Step 1: Writing to memory (Read/Write protection) ---");
*mem_ptr.add(0) = 42;
println!("Wrote value: {}", *mem_ptr.add(0));
}
// --- 2. Change protection to READ-ONLY ---
// Now, we use mprotect to remove the write permission.
let result = unsafe {
println!("\n--- Step 2: Changing memory protection to Read-Only ---");
libc::mprotect(memory, page_size, libc::PROT_READ)
};
if result != 0 {
panic!("mprotect failed to set read-only");
}
println!("Memory is now Read-Only.");
// Reading should still be fine.
unsafe {
println!("Value read from memory: {}", *mem_ptr.add(0));
}
// --- 3. Demonstrate the effect of READ-ONLY protection ---
// If you uncomment the following lines, the program will crash with a
// segmentation fault, because we are illegally trying to write to a
// read-only memory page. This is the expected behavior.
/*
unsafe {
println!("\n--- Step 3: Attempting to write to Read-Only memory (EXPECTED TO CRASH) ---");
*mem_ptr.add(0) = 99; // This line will cause a segmentation fault
println!("This line will not be reached.");
}
*/
println!("\n--- Step 3: Skipped write-attempt to Read-Only memory. ---");
println!("(Uncomment the code in main.rs to see the segmentation fault)");
// --- 4. Change protection back to READ-WRITE ---
let result = unsafe {
println!("\n--- Step 4: Changing memory back to Read/Write ---");
libc::mprotect(memory, page_size, libc::PROT_READ | libc::PROT_WRITE)
};
if result != 0 {
panic!("mprotect failed to set read-write");
}
println!("Memory is now Read/Write again.");
// Now we can write to it again successfully.
unsafe {
*mem_ptr.add(0) = 123;
println!("Wrote new value: {}", *mem_ptr.add(0));
}
// --- 5. Clean up ---
// Finally, we must unmap the memory to release it back to the OS.
unsafe {
println!("\n--- Step 5: Cleaning up ---");
libc::munmap(memory, page_size);
println!("Memory unmapped successfully.");
}
}
Result from Redox
Attempting to map memory at fixed address: 0x100000000
thread 'main' panicked at src/main.rs:41:9:
mmap with MAP_FIXED returned a different address! Expected 0x100000000, got 0x16000
stack backtrace:
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
redoxerd: exit status: 101
## redoxer (failure) ##
System page size: 4096 bytes
Attempting to map memory at fixed address: 0x100000000
thread 'main' panicked at src/main.rs:41:9:
mmap with MAP_FIXED returned a different address! Expected 0x100000000, got 0x16000
stack backtrace:
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
redoxer cargo: exit status: 1
Result from Linux
Attempting to map memory at fixed address: 0x100000000
Successfully allocated one page at address: 0x100000000
--- Step 1: Writing to memory (Read/Write protection) ---
Wrote value: 42
--- Step 2: Changing memory protection to Read-Only ---
Memory is now Read-Only.
Value read from memory: 42
--- Step 3: Skipped write-attempt to Read-Only memory. ---
(Uncomment the code in main.rs to see the segmentation fault)
--- Step 4: Changing memory back to Read/Write ---
Memory is now Read/Write again.
Wrote new value: 123
--- Step 5: Cleaning up ---
Memory unmapped successfully.
If the code is changed
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS | 0x4
It working just like Linux part