gate: pass on macOS: tests use /tmp, gatewayd finds its uid without /proc

M5a task 16. The work is Ornith's, from the stopped run; the design
model checked it against the task file and committed it once the
memoryd lint that stopped the gate was fixed (44bdcbd).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-25 16:41:44 -07:00
co-authored by Claude Opus 5.5
parent d47fb32483
commit 7d5c5a8d85
4 changed files with 23 additions and 6 deletions
+13 -4
View File
@@ -163,7 +163,7 @@ pub fn read_checked(path: &Path, between: &dyn Fn()) -> Result<Zeroizing<Vec<u8>
// 5. `if (opened.dev(), opened.ino()) != (named.dev(), named.ino())`:
// "<path> changed while it was read".
// 6. The owner and mode checks from the original file check, word for word, on `opened` (not
// on `named`): the uid of "/proc/self"; "<path> is not owned by the user gatewayd runs as";
// on `named`): the uid of `user_id()`; "<path> is not owned by the user gatewayd runs as";
// "<path> has mode <mode:03o>; only the owner may read it (0600 or 0400)".
// 7. `let mut bytes = Zeroizing::new(Vec::new());` then `file.read_to_end(&mut bytes)`, an
// Err(e) is "cannot read <path>: <e>". Ok(bytes).
@@ -187,9 +187,7 @@ pub fn read_checked(path: &Path, between: &dyn Fn()) -> Result<Zeroizing<Vec<u8>
if (opened.dev(), opened.ino()) != (named.dev(), named.ino()) {
return Err(format!("{} changed while it was read", path.display()));
}
let owner = std::fs::metadata("/proc/self")
.map_err(|e| format!("cannot read /proc/self: {}", e))?
.uid();
let owner = user_id()?;
if opened.uid() != owner {
return Err(format!(
"{} is not owned by the user gatewayd runs as",
@@ -210,6 +208,17 @@ pub fn read_checked(path: &Path, between: &dyn Fn()) -> Result<Zeroizing<Vec<u8>
Ok(bytes)
}
/// The user id this process runs as: the owner of a pipe it has just made. `std` has no call for
/// it, and `/proc/self` is Linux only.
fn user_id() -> Result<u32, String> {
let (reader, _writer) = std::io::pipe().map_err(|e| format!("cannot make a pipe: {e}"))?;
let pipe = std::fs::File::from(std::os::fd::OwnedFd::from(reader));
let meta = pipe
.metadata()
.map_err(|e| format!("cannot read a pipe: {e}"))?;
Ok(meta.uid())
}
/// The text without one trailing newline; not empty; UTF-8.
fn value(bytes: Vec<u8>) -> Result<Secret, String> {
// UTF-8 (else an error), one trailing newline removed, not empty. Keep the bytes in `Zeroizing`
+4 -2
View File
@@ -42,10 +42,12 @@ fn a_command_killed_by_a_signal_says_so() {
fn the_command_runs_in_cwd_or_in_tmp() {
let dir = TempDir::new("cwd");
let here = dir.path().to_str().unwrap();
// `pwd` prints the path with symbolic links resolved: on macOS, /tmp is /private/tmp.
let real = |path: &str| std::fs::canonicalize(path).unwrap().display().to_string();
let ran = sh("pwd", Some(here));
assert_eq!(ran.stdout, format!("{here}\n\n[exit 0]"));
assert_eq!(ran.stdout, format!("{}\n\n[exit 0]", real(here)));
let ran = sh("pwd", None);
assert_eq!(ran.stdout, "/tmp\n\n[exit 0]");
assert_eq!(ran.stdout, format!("{}\n\n[exit 0]", real("/tmp")));
}
#[test]