Skip to content

Commit 1a50313

Browse files
authored
Fix the type of the mode argument in open (#715)
When calling `libc::open`, cast the mode argument to `libc::mode_t` rather than `libc::c_long`, as `mode_t` is what it's documented as taking. This fixes an undefined-behavior error when run under miri.
1 parent 2f7c024 commit 1a50313

File tree

1 file changed

+38
-17
lines changed

1 file changed

+38
-17
lines changed

src/backend/libc/fs/syscalls.rs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,25 @@ pub(crate) fn open(path: &CStr, oflags: OFlags, mode: Mode) -> io::Result<OwnedF
123123
if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() {
124124
return open_via_syscall(path, oflags, mode);
125125
}
126-
unsafe {
127-
// Pass `mode` as a `c_ulong` even if `mode_t` is narrower, since
128-
// `c::open` is declared as a variadic function and narrower
129-
// arguments are promoted.
130-
ret_owned_fd(c::open(
131-
c_str(path),
132-
bitflags_bits!(oflags),
133-
mode.bits() as c::c_ulong,
134-
))
135-
}
126+
127+
// On these platforms, `mode_t` is `u16` and can't be passed directly to
128+
// a variadic function.
129+
#[cfg(any(
130+
apple,
131+
freebsdlike,
132+
all(target_os = "android", target_pointer_width = "32")
133+
))]
134+
let mode: c::c_uint = mode.bits().into();
135+
136+
// Otherwise, cast to `mode_t` as that's what `open` is documented to take.
137+
#[cfg(not(any(
138+
apple,
139+
freebsdlike,
140+
all(target_os = "android", target_pointer_width = "32")
141+
)))]
142+
let mode: c::mode_t = mode.bits() as _;
143+
144+
unsafe { ret_owned_fd(c::open(c_str(path), bitflags_bits!(oflags), mode)) }
136145
}
137146

138147
/// Use a direct syscall (via libc) for `openat`.
@@ -177,15 +186,30 @@ pub(crate) fn openat(
177186
if oflags.contains(OFlags::TMPFILE) && crate::backend::if_glibc_is_less_than_2_25() {
178187
return openat_via_syscall(dirfd, path, oflags, mode);
179188
}
189+
190+
// On these platforms, `mode_t` is `u16` and can't be passed directly to
191+
// a variadic function.
192+
#[cfg(any(
193+
apple,
194+
freebsdlike,
195+
all(target_os = "android", target_pointer_width = "32")
196+
))]
197+
let mode: c::c_uint = mode.bits().into();
198+
199+
// Otherwise, cast to `mode_t` as that's what `open` is documented to take.
200+
#[cfg(not(any(
201+
apple,
202+
freebsdlike,
203+
all(target_os = "android", target_pointer_width = "32")
204+
)))]
205+
let mode: c::mode_t = mode.bits() as _;
206+
180207
unsafe {
181-
// Pass `mode` as a `c_ulong` even if `mode_t` is narrower, since
182-
// `c::openat` is declared as a variadic function and narrower
183-
// arguments are promoted.
184208
ret_owned_fd(c::openat(
185209
borrowed_fd(dirfd),
186210
c_str(path),
187211
bitflags_bits!(oflags),
188-
mode.bits() as c::c_ulong,
212+
mode,
189213
))
190214
}
191215
}
@@ -924,9 +948,6 @@ pub(crate) fn chmodat(
924948
return Err(io::Errno::INVAL);
925949
}
926950
unsafe {
927-
// Pass `mode` as a `c_ulong` even if `mode_t` is narrower, since
928-
// `c::syscall` is declared as a variadic function and narrower
929-
// arguments are promoted.
930951
ret(fchmodat(
931952
borrowed_fd(dirfd),
932953
c_str(path),

0 commit comments

Comments
 (0)