Add O_NOFOLLOW flag support
This commit is contained in:
parent
0c1448d75f
commit
69b9eab4ab
3 changed files with 55 additions and 3 deletions
|
|
@ -266,7 +266,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
let path = this.read_pointer(&args[0])?;
|
||||
let path_raw = this.read_pointer(&args[0])?;
|
||||
let path = this.read_path_from_c_str(path_raw)?;
|
||||
let flag = this.read_scalar(&args[1])?.to_i32()?;
|
||||
|
||||
let mut options = OpenOptions::new();
|
||||
|
|
@ -366,14 +367,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
return Ok(-1);
|
||||
}
|
||||
}
|
||||
|
||||
let o_nofollow = this.eval_libc_i32("O_NOFOLLOW");
|
||||
if flag & o_nofollow == o_nofollow {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
options.custom_flags(libc::O_NOFOLLOW);
|
||||
}
|
||||
// Strictly speaking, this emulation is not equivalent to the O_NOFOLLOW flag behavior:
|
||||
// the path could change between us checking it here and the later call to `open`.
|
||||
// But it's good enough for Miri purposes.
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
// O_NOFOLLOW only fails when the trailing component is a symlink;
|
||||
// the entire rest of the path can still contain symlinks.
|
||||
if path.is_symlink() {
|
||||
let eloop = this.eval_libc("ELOOP");
|
||||
this.set_last_error(eloop)?;
|
||||
return Ok(-1);
|
||||
}
|
||||
}
|
||||
mirror |= o_nofollow;
|
||||
}
|
||||
|
||||
// If `flag` is not equal to `mirror`, there is an unsupported option enabled in `flag`,
|
||||
// then we throw an error.
|
||||
if flag != mirror {
|
||||
throw_unsup_format!("unsupported flags {:#x}", flag & !mirror);
|
||||
}
|
||||
|
||||
let path = this.read_path_from_c_str(path)?;
|
||||
|
||||
// Reject if isolation is enabled.
|
||||
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
|
||||
this.reject_in_isolation("`open`", reject_with)?;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,11 @@ use std::os::unix::ffi::OsStrExt;
|
|||
mod utils;
|
||||
|
||||
fn main() {
|
||||
test_readlink();
|
||||
test_nofollow_symlink();
|
||||
}
|
||||
|
||||
fn test_readlink() {
|
||||
let bytes = b"Hello, World!\n";
|
||||
let path = utils::prepare_with_content("miri_test_fs_link_target.txt", bytes);
|
||||
let expected_path = path.as_os_str().as_bytes();
|
||||
|
|
@ -49,3 +54,18 @@ fn main() {
|
|||
assert_eq!(res, -1);
|
||||
assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
|
||||
}
|
||||
|
||||
fn test_nofollow_symlink() {
|
||||
let bytes = b"Hello, World!\n";
|
||||
let path = utils::prepare_with_content("test_nofollow_symlink_target.txt", bytes);
|
||||
|
||||
let symlink_path = utils::prepare("test_nofollow_symlink.txt");
|
||||
std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
|
||||
|
||||
let symlink_cpath = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
|
||||
|
||||
let ret = unsafe { libc::open(symlink_cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
|
||||
assert_eq!(ret, -1);
|
||||
let err = io::Error::last_os_error().raw_os_error().unwrap();
|
||||
assert_eq!(err, libc::ELOOP);
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ fn main() {
|
|||
test_sync_file_range();
|
||||
test_isatty();
|
||||
test_read_and_uninit();
|
||||
test_nofollow_not_symlink();
|
||||
}
|
||||
|
||||
fn test_file_open_unix_allow_two_args() {
|
||||
|
|
@ -423,3 +424,11 @@ fn test_read_and_uninit() {
|
|||
remove_file(&path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn test_nofollow_not_symlink() {
|
||||
let bytes = b"Hello, World!\n";
|
||||
let path = utils::prepare_with_content("test_nofollow_not_symlink.txt", bytes);
|
||||
let cpath = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||
let ret = unsafe { libc::open(cpath.as_ptr(), libc::O_NOFOLLOW | libc::O_CLOEXEC) };
|
||||
assert!(ret >= 0);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue