Skip to content

IPC grate: [3i|copy] range invalid … len=144 — 144-byte struct stat copy rejected #173

@vidyalakshmir

Description

@vidyalakshmir

Summary

Under the IPC grate, several tests print [3i|copy] range invalid: addr=0x…, len=144,
what="destination" and fail. 144 is sizeof(struct stat) on x86-64 Linux, i.e. the IPC grate's
cross-cage copy of a struct stat result buffer back into the calling cage is being rejected by
3i's destination-range validation.

Affected tests

cp/file-perm-race, dd/skip-seek-past-file, misc/cut, misc/wc, misc/shuf, misc/sort,
misc/readlink-fp-loop, misc/runcon-no-reorder, misc/nohup (~8–9 tests).

Reproduction

  ./run_default_tests.sh --grate ipc misc/wc
  Output:
  [3i|copy] range invalid: addr=0x775668800f80, len=144, what="destination"
  wc: test a0 failed: exit status mismatch:  expected 0, got 1

Root cause (traced)

The error comes from threei/src/threei.rs:759, inside _validate_range_rw, called from
copy_data_between_cages at threei.rs:948:
if let Err(code) = _validate_range_rw(destcage, destaddr, copy_len, "destination") {
return code;
}
_validate_range_rw → check_addr_rw(destcage, destaddr, 144) fails — the destination address (the
cage's struct stat buffer) is not recognized as readable+writable in destcage's vmmap.

len = 144 pins this to a struct stat copy (the timespec[2] buffer for futimens would be 32
bytes). So when the IPC grate handles fstat/stat/lstat and copies the kernel struct stat back to
the cage via copy_data_between_cages, the destcage / destaddr pair doesn't validate.

The bug

The grate's IPC fstat branch passes an already-host-translated pointer (arg2) into
copy_data_between_cages, whose destaddr parameter must be a guest address — the function does
its own vmmap-based validation/translation. The two layers disagree on the pointer convention.
(Note rawposix's own fstat_syscall sidesteps this by casting the host pointer directly via
sc_convert_addr_to_statdata — it never calls copy_data_between_cages.)

Fix options for the grate

  • Simplest: since arg2 is already a valid host pointer into the cage's linear memory, skip
    copy_data_between_cages entirely and write the 144 bytes directly:
    unsafe { core::ptr::copy_nonoverlapping(buf.as_ptr(), arg2 as *mut u8, STAT_SIZE); }
  • Or: if the grate must use copy_data_between_cages, it needs the guest statbuf pointer — which
    means fstat shouldn't be pre-translated in glibc for the grate path, a much bigger change.

The direct-write fix is the right one and is consistent with how rawposix already handles the
statbuf. Worth auditing every other copy_data_between_cages call in the grate for the same
host/guest confusion (fstatfs_handler and any socket-addr copy-outs are prime suspects).

Impact

~8–9 IPC-grate failures.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions