Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
afa13ba
Honor GRATE_MEMORY_FLAG in mmap_syscall and brk_syscall
rennergade May 10, 2026
1d2d47b
sc_convert_addr_to_sys: short-circuit arg=0 to cage base
rennergade May 10, 2026
9dcdcb0
mmap-flag cage test: stdout prints + fflush to localize failure
rennergade May 10, 2026
8b78e00
mmap-flag grate: log each interception with args + return value
rennergade May 10, 2026
45ea1bb
mmap_syscall: temporary eprintln diagnostic
rennergade May 11, 2026
7f1f583
sc_convert_addr_to_sys: cross-cage base lookup, not "use as-is"
rennergade May 11, 2026
ae73bd8
mmap_syscall: re-add diagnostic eprintln to identify why MAP_FIXED br…
rennergade May 11, 2026
790917c
Switch mmap/brk runtime check from FLAG-bit to value-range
rennergade May 11, 2026
1d7004a
mmap-flag grate test: only apply FLAG to fd-backed mmaps
rennergade May 11, 2026
c4ba2a7
diagnostics: hex eprintln in mmap_syscall + grate, to compare with/wi…
rennergade May 11, 2026
304f5a4
mmap-flag grate: fix target cageid (use arg5cage, not handler's grate…
rennergade May 11, 2026
ef4846f
Strip diagnostic eprintlns and grate/cage debug printfs
rennergade May 11, 2026
3829f3a
TEMP diagnostic: trace mmap_syscall path-by-path
rennergade May 11, 2026
a11fad8
Revert "TEMP diagnostic: trace mmap_syscall path-by-path"
rennergade May 11, 2026
d1b9877
Address review: repurpose sc_convert_uaddr_to_host instead of adding …
rennergade May 11, 2026
2368331
vmmap: handle cage uaddr in calculate_page_range (issue #1186 part 2)
rennergade May 11, 2026
0192789
copy_data_between_cages: translate src/dest before validate + memcpy
rennergade May 11, 2026
64e92c0
Merge remote-tracking branch 'origin/main' into grate-memory-flag-run…
rennergade May 13, 2026
d2ec548
Trace popen handler table inheritance
rennergade May 13, 2026
310c803
Trace dashmap handler table dup2 state
rennergade May 13, 2026
de012db
Trace threei handler table backend for popen
rennergade May 13, 2026
d64f844
Remove popen trace instrumentation
rennergade May 13, 2026
f706ed6
Fix grate memory runtime formatting
rennergade May 13, 2026
619179d
Leave hashmap handler table unchanged
rennergade May 13, 2026
7b65359
Address grate memory flag review comments
rennergade May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 62 additions & 32 deletions src/rawposix/src/fs_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,13 +829,6 @@ pub extern "C" fn mmap_syscall(
off_arg: u64,
off_cageid: u64,
) -> i32 {
let addr = {
if addr_arg == 0 {
0 as *mut u8
} else {
sc_convert_to_u8_mut(addr_arg, addr_cageid, cageid)
}
};
let len = sc_convert_sysarg_to_usize(len_arg, len_cageid, cageid);
let prot = sc_convert_sysarg_to_i32(prot_arg, prot_cageid, cageid);
let mut flags = sc_convert_sysarg_to_i32(flags_arg, flags_cageid, cageid);
Expand Down Expand Up @@ -871,9 +864,11 @@ pub extern "C" fn mmap_syscall(
lind_debug_panic("mmap protection flag PROT_EXEC is not allowed in Lind");
}

// check if the provided address is multiple of pages
let rounded_addr = round_up_page(addr as u64);
if rounded_addr != addr as u64 {
// Page-align check on the low bits of addr_arg. Page alignment is a
// numeric property of the low bits regardless of which cage's base
// address gets added, since base_address is itself page-aligned.
let rounded_addr = round_up_page(addr_arg);
if rounded_addr != addr_arg {
return syscall_error(Errno::EINVAL, "mmap", "address it not aligned");
}

Expand All @@ -889,31 +884,60 @@ pub extern "C" fn mmap_syscall(
// round up length to be multiple of pages
let rounded_length = round_up_page(len as u64);

let mut useraddr = addr as u32;
// if MAP_FIXED is not set, then we need to find an address for the user
// Resolve (useraddr in calling cage, sysaddr host pointer). Addresses
// arrive as either a cage uaddr (≤ u32::MAX, from cage-side mmap.c) or
// a host sysaddr (above u32::MAX, from a grate-forwarded call whose
// GRATE_MEMORY_FLAG was already consumed by glibc's make_threei_call /
// TRANSLATE_ARG_TO_HOST). sc_convert_addr_to_sys handles the split.
let mut useraddr: u32;
let sysaddr: usize;

if flags & MAP_FIXED as i32 == 0 {
let vmmap = cage.vmmap.write();
let result;
// No fixed address — runtime picks via the calling cage's vmmap.
// Use addr_arg as a hint; if a grate forwarded a host sysaddr
// (above u32 range), convert it back to a cage uaddr first.
let hint_useraddr = if addr_arg > u32::MAX as u64 {
match sc_convert_sys_to_user(addr_arg as usize, cageid) {
Ok(u) => u,
Err(_) => 0,
}
} else {
addr_arg as u32
};

// pick an address of appropriate size, anywhere
if useraddr == 0 {
result = vmmap.find_map_space(rounded_length as u32 >> PAGESHIFT, 1);
let vmmap = cage.vmmap.write();
let result = if hint_useraddr == 0 {
vmmap.find_map_space(rounded_length as u32 >> PAGESHIFT, 1)
} else {
// use address user provided as hint to find address
result = vmmap.find_map_space_with_hint(
vmmap.find_map_space_with_hint(
rounded_length as u32 >> PAGESHIFT,
1,
addr as u32 >> PAGESHIFT,
);
}
hint_useraddr >> PAGESHIFT,
)
};

// did not find desired memory region
if result.is_none() {
return syscall_error(Errno::ENOMEM, "mmap", "no memory");
}

let space = result.unwrap();
useraddr = (space.start() << PAGESHIFT) as u32;
useraddr = (result.unwrap().start() << PAGESHIFT) as u32;
sysaddr = vmmap.user_to_sys(useraddr);
drop(vmmap);
} else {
// Caller specified an exact address. Use the flag-aware helper so
// a grate-supplied addr is resolved against the grate's base, and
// a cage-supplied addr against the calling cage's base.
sysaddr = match sc_convert_addr_to_sys(addr_arg, addr_cageid, cageid) {
Ok(s) => s,
Err(e) => return syscall_error(e, "mmap", "invalid addr"),
};
// Derive the calling cage's uaddr for the return value + vmmap
// bookkeeping. Errors here mean the sysaddr is outside the cage's
// linear memory range — invalid for MAP_FIXED in this cage.
useraddr = match sc_convert_sys_to_user(sysaddr, cageid) {
Ok(u) => u,
Err(e) => return syscall_error(e, "mmap", "addr outside cage"),
};
}

flags |= MAP_FIXED as i32;
Expand All @@ -923,12 +947,6 @@ pub extern "C" fn mmap_syscall(
return syscall_error(Errno::EINVAL, "mmap", "invalid flags");
}

let vmmap = cage.vmmap.read();

let sysaddr = vmmap.user_to_sys(useraddr);

drop(vmmap);

if rounded_length > 0 {
if flags & MAP_ANONYMOUS as i32 > 0 {
fildes = -1;
Expand Down Expand Up @@ -1188,7 +1206,19 @@ pub extern "C" fn brk_syscall(
arg6: u64,
arg6_cageid: u64,
) -> i32 {
let brk = sc_convert_sysarg_to_i32(brk_arg, brk_cageid, cageid);
// Cage-side glibc brk.c passes a raw uaddr (low 32 bits); the runtime
// page-aligns it and compares against vmmap.heap_start in user space.
// A grate forwarding the call via make_threei_call goes through
// glibc's TRANSLATE_ARG_TO_HOST which produces a host sysaddr (above
// u32 range) — convert that back to a cage uaddr before proceeding.
let brk = if brk_arg > u32::MAX as u64 {
match sc_convert_sys_to_user(brk_arg as usize, cageid) {
Ok(u) => u as i32,
Err(e) => return syscall_error(e, "brk", "addr outside cage"),
}
} else {
sc_convert_sysarg_to_i32(brk_arg, brk_cageid, cageid)
};
// would sometimes check, sometimes be a no-op depending on the compiler settings
if !(sc_unusedarg(arg2, arg2_cageid)
&& sc_unusedarg(arg3, arg3_cageid)
Expand Down
7 changes: 7 additions & 0 deletions src/sysdefs/src/constants/lind_platform_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ pub const MAXFD: usize = 1024; // Maximum file descriptors per cage
pub const MAX_LINEAR_MEMORY_SIZE: u64 = 0xFFFF_FFFF;
/// Placeholder for unused syscall argument
pub const UNUSED_ARG: u64 = 0xDEADBEEF_DEADBEEF;
/// MSB of a syscall arg's cageid: signals that the arg should be treated as a
/// host-side reference into the named cage's linear memory, not as a uaddr
/// in the calling cage's memory. Mirrors `LIND_ARG_TRANSLATE_FLAG` in
/// `src/glibc/lind_syscall/addr_translation.h`.
pub const GRATE_MEMORY_FLAG: u64 = 1u64 << 63;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this. Need to discuss

/// Mask to recover the actual cageid by clearing `GRATE_MEMORY_FLAG`.
pub const LIND_ARG_CAGEID_MASK: u64 = !GRATE_MEMORY_FLAG;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to make a macro that uses this?

/// Placeholder for unused cage/grate ID
pub const UNUSED_ID: u64 = 0xCAFEBABE_CAFEBABE;
/// Placeholder for unused syscall name
Expand Down
83 changes: 83 additions & 0 deletions src/typemap/src/datatype_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,89 @@ pub fn sc_convert_to_u8_mut(arg: u64, arg_cageid: u64, cageid: u64) -> *mut u8 {
arg as *mut u8
}

/// Resolve a (uaddr, cageid) pair to a host system address, honoring
/// `GRATE_MEMORY_FLAG`.
///
/// For path-style buffer args the runtime can just dereference the address as
/// a host pointer (see `get_cstr`) — bytes are bytes. For address args that
/// the runtime *interprets* rather than dereferences (mmap, munmap, mprotect,
/// brk, shmat, shmdt), we need the actual host system address.
///
/// Distinguishes which form of address `arg` carries:
///
/// - **u32 range (`arg <= u32::MAX`)**: a uaddr in the calling cage's linear
/// memory. This is what cage-side glibc wrappers (e.g. `mmap.c`'s
/// `(uintptr_t) addr`) pass — wasm32 uaddrs fit in u32. We translate via
/// the calling cage's vmmap base.
/// - **Above u32 range**: a host system address already, produced by glibc's
/// `TRANSLATE_ARG_TO_HOST` macro inside `make_threei_call` (e.g. when a
/// grate forwards with `GRATE_MEMORY_FLAG` set). By the time the runtime
/// sees the call, the FLAG bit has been stripped from `arg_cageid` and the
/// arg is the resolved host pointer. Use as-is.
///
/// The ranges don't overlap: cage linear memory occupies `[base, base + 4GB]`
/// on the host, and host bases are typically far above 4GB.
///
/// arg=0 is special-cased to "start of the calling cage's memory" — a NULL
/// host pointer would be meaningless.
///
/// ## Returns
/// - `Ok(sysaddr)` host system address.
/// - `Err(Errno::EINVAL)` if the calling cage can't be looked up or its vmmap
/// has no base address yet (only on the uaddr branch).
pub fn sc_convert_addr_to_sys(arg: u64, arg_cageid: u64, cageid: u64) -> Result<usize, Errno> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we already have a sc_convert_addr_to_host ? We can just update that directly, they are not used anywhere else.

#[cfg(feature = "secure")]
{
if !validate_cageid(arg_cageid, cageid) {
return Err(Errno::EINVAL);
}
}
let _ = arg_cageid; // FLAG is consumed glibc-side; runtime distinguishes by value range.

// arg=0 has cage-relative "start of cage memory" meaning — a NULL host
// pointer would be meaningless. Anchor to the calling cage's base so
// early_init_stack-style mmaps (addr=0, MAP_FIXED) work.
if arg == 0 {
let cage = get_cage(cageid).ok_or(Errno::EINVAL)?;
let vmmap = cage.vmmap.read();
let base = vmmap.base_address.ok_or(Errno::EINVAL)?;
return Ok(base);
}

// Distinguish uaddr (≤ u32::MAX) from host sysaddr (above). See doc above.
if arg <= u32::MAX as u64 {
let cage = get_cage(cageid).ok_or(Errno::EINVAL)?;
let vmmap = cage.vmmap.read();
let base = vmmap.base_address.ok_or(Errno::EINVAL)?;
return Ok(base + arg as usize);
}

Ok(arg as usize)
}

/// Inverse of `sc_convert_addr_to_sys` — translate a host system address back
/// to a uaddr in the named cage's linear memory. Used for return values of
/// mmap-family syscalls and for bookkeeping into the cage's vmmap.
///
/// ## Arguments
/// - `sysaddr`: the host system address.
/// - `cageid`: the cage whose user-address space we want.
///
/// ## Returns
/// - `Ok(uaddr)` truncated to u32 (cage user addresses fit in 32 bits on
/// wasm32 lind).
/// - `Err(Errno::EINVAL)` if the cage can't be looked up, its vmmap has no
/// base, or `sysaddr` is below the cage's base.
pub fn sc_convert_sys_to_user(sysaddr: usize, cageid: u64) -> Result<u32, Errno> {
let cage = get_cage(cageid).ok_or(Errno::EINVAL)?;
let vmmap = cage.vmmap.read();
let base = vmmap.base_address.ok_or(Errno::EINVAL)?;
if sysaddr < base {
return Err(Errno::EINVAL);
}
Ok((sysaddr - base) as u32)
}

/// This function translates the buffer pointer from user buffer address to system address, because we are
/// transferring between 32-bit WASM environment to 64-bit kernel
///
Expand Down
51 changes: 51 additions & 0 deletions tests/grate-tests/simple-tests/mmap-flag.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* Cage side of the mmap-with-GRATE_MEMORY_FLAG test.
*
* fd-backed mmap → write → read → munmap round-trip. The companion
* grate forwards this call to RawPOSIX with `arg1cage | GRATE_MEMORY_FLAG`,
* exercising the runtime's flag-aware path in mmap_syscall.
*
* Anonymous mmaps (including the runtime's own pre-main stack setup)
* are forwarded by the grate without the flag and aren't exercised here.
*/

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#define FILE_PATH "mmap-flag.tmp"

int main(void) {
const size_t size = 4096;

int fd = open(FILE_PATH, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd < 0) {
return 1;
}
if (ftruncate(fd, size) != 0) {
return 1;
}

void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (p == MAP_FAILED) {
return 1;
}

memset(p, 0x42, size);
for (size_t i = 0; i < size; i++) {
if (((unsigned char *)p)[i] != 0x42) {
return 1;
}
}

if (munmap(p, size) != 0) {
return 1;
}

close(fd);
unlink(FILE_PATH);

printf("[Cage|mmap-flag] PASS\n");
return 0;
}
Loading