Skip to content

cap-primitives 4.0.3 panics with TryFromIntError on a negative macOS st_rdev #427

Description

@zackees

Summary

ImplMetadataExt::from_rustix panics with TryFromIntError(()) on macOS because it converts st_rdev with u64::try_from(...).unwrap(), while the dev field a few lines above guards the same signedness problem explicitly.

Where

cap-primitives 4.0.3 (newest 4.x on crates.io), rustix backend, src/rustix/fs/metadata_ext.rs:171:

dev: if stat.st_dev < 0 {            // ← guarded
    i64::try_from(stat.st_dev).unwrap() as u64
} else {
    u64::try_from(stat.st_dev).unwrap()
},
ino: stat.st_ino.into(),
...
rdev: u64::try_from(stat.st_rdev).unwrap(),   // ← line 171, unguarded

The asymmetry is the whole report. dev handles a negative dev_t and carries a comment acknowledging the platform difference — "platforms where it's unsigned since the first branch here will never be taken" — while rdev, immediately below it, assumes non-negative and unwraps. On macOS dev_t is a signed i32, so st_rdev can be negative and the conversion fails.

Observed

Reached from cap_std's symlink_metadata path, matching the backtrace shape in #328:

called `Result::unwrap()` on an `Err` value: TryFromIntError(())
  at cap-primitives-4.0.3/src/rustix/fs/metadata_ext.rs:171

Environment: x86_64-apple-darwin inside a macOS Ventura Recovery guest under QEMU/KVM, stat'ing ordinary files in a temporary directory. The filesystem there reports a negative st_rdev for regular files, which is what triggers it.

I cannot offer a minimal reproduction. I hit this from CI on a Linux host driving a macOS guest, and I have no macOS machine to narrow it to a specific filesystem or file. I am reporting the code asymmetry, which is verifiable by reading the twelve lines above, rather than claiming to have characterised the trigger. If st_rdev is negative only on some filesystems, the guard below is still the right shape.

Relation to #328

Same file, same error type, same class of assumption — an unguarded try_from on a field that is not always non-negative. That one was about negative timestamps; this is a sibling field that the same reasoning applies to. I did not check whether the dev guard was added by that fix, so I am not claiming rdev was missed by it — only that it has the same problem now.

Suggested fix

Guard st_rdev as st_dev is:

rdev: if stat.st_rdev < 0 {
    i64::try_from(stat.st_rdev).unwrap() as u64
} else {
    u64::try_from(stat.st_rdev).unwrap()
},

or restructure both into a small helper so the next field cannot repeat the mistake. I have not opened a PR because I cannot test the macOS path; the change is yours to make with a reproduction you can actually run.

Impact beyond tests

This is not only a test-fixture problem. It panics inside symlink_metadata, so any cap-std consumer stat'ing a file whose st_rdev is negative fails at runtime on macOS, not merely under a profiler.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions