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`