mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
fix(git): reject process-relative procfs config
This commit is contained in:
@@ -107,6 +107,82 @@ fn symlink_route_hops(path: &Path) -> io::Result<Vec<SymlinkRouteHop>> {
|
||||
Ok(hops)
|
||||
}
|
||||
|
||||
/// Detect Linux procfs routes whose target can differ in a later Git process.
|
||||
///
|
||||
/// Existing procfs-hosted symlinks are process-dependent, and missing procfs
|
||||
/// descendants can materialize when the Git child's PID or descriptors exist.
|
||||
pub(super) fn route_contains_process_relative_procfs_path(path: &Path) -> io::Result<bool> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let hops = symlink_route_hops(path)?;
|
||||
for hop in &hops {
|
||||
if symlink_is_on_procfs(&hop.entry)? {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
if missing_descendant_is_below_procfs(path)? {
|
||||
return Ok(true);
|
||||
}
|
||||
for hop in hops {
|
||||
if missing_descendant_is_below_procfs(&hop.target)?
|
||||
|| missing_descendant_is_below_procfs(&hop.projected)?
|
||||
{
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let _ = path;
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn symlink_is_on_procfs(path: &Path) -> io::Result<bool> {
|
||||
filesystem_is_procfs(
|
||||
path,
|
||||
rustix::fs::OFlags::PATH | rustix::fs::OFlags::NOFOLLOW | rustix::fs::OFlags::CLOEXEC,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn missing_descendant_is_below_procfs(path: &Path) -> io::Result<bool> {
|
||||
match std::fs::symlink_metadata(path) {
|
||||
Ok(_) => return Ok(false),
|
||||
Err(error) if is_missing_path_error(&error) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
for ancestor in path.ancestors().skip(1) {
|
||||
match std::fs::symlink_metadata(ancestor) {
|
||||
Ok(_) => match filesystem_is_procfs(
|
||||
ancestor,
|
||||
rustix::fs::OFlags::PATH | rustix::fs::OFlags::CLOEXEC,
|
||||
) {
|
||||
Ok(is_procfs) => return Ok(is_procfs),
|
||||
Err(error) if is_missing_path_error(&error) => {}
|
||||
Err(error) => return Err(error),
|
||||
},
|
||||
Err(error) if is_missing_path_error(&error) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
Err(invalid_data("authority path has no existing ancestor"))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn filesystem_is_procfs(path: &Path, flags: rustix::fs::OFlags) -> io::Result<bool> {
|
||||
let fd = rustix::fs::open(path, flags, rustix::fs::Mode::empty()).map_err(io::Error::from)?;
|
||||
let filesystem = rustix::fs::fstatfs(&fd).map_err(io::Error::from)?;
|
||||
Ok(filesystem.f_type == rustix::fs::PROC_SUPER_MAGIC)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn is_missing_path_error(error: &io::Error) -> bool {
|
||||
matches!(
|
||||
error.kind(),
|
||||
io::ErrorKind::NotFound | io::ErrorKind::NotADirectory
|
||||
)
|
||||
}
|
||||
|
||||
fn collect_symlink_route_hops(
|
||||
path: &Path,
|
||||
depth: usize,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
use pretty_assertions::assert_eq;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::Path;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::read_link_if_symlink;
|
||||
#[cfg(target_os = "linux")]
|
||||
use super::route_contains_process_relative_procfs_path;
|
||||
|
||||
#[test]
|
||||
fn ordinary_paths_are_not_treated_as_failed_symlink_reads() {
|
||||
@@ -16,3 +22,59 @@ fn ordinary_paths_are_not_treated_as_failed_symlink_reads() {
|
||||
);
|
||||
assert_eq!(read_link_if_symlink(&file).expect("ordinary file"), None);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
#[test]
|
||||
fn process_relative_procfs_detection_follows_aliases_without_rejecting_ordinary_paths() {
|
||||
use std::os::unix::fs::symlink;
|
||||
|
||||
let fixture = tempfile::tempdir().expect("fixture");
|
||||
let external = fixture.path().join("external");
|
||||
let ordinary_alias = fixture.path().join("ordinary-alias");
|
||||
let procfs_alias = fixture.path().join("procfs-alias");
|
||||
let procfs_root_alias = fixture.path().join("procfs-root-alias");
|
||||
let chained_alias = fixture.path().join("chained-alias");
|
||||
let literal = fixture.path().join("proc/self/cwd/config");
|
||||
std::fs::create_dir_all(&external).expect("external directory");
|
||||
std::fs::create_dir_all(literal.parent().expect("literal parent"))
|
||||
.expect("literal directories");
|
||||
std::fs::write(&literal, "[safe]\n").expect("literal config");
|
||||
symlink(&external, &ordinary_alias).expect("ordinary alias");
|
||||
symlink("/proc/self/cwd", &procfs_alias).expect("procfs alias");
|
||||
symlink("/proc", &procfs_root_alias).expect("procfs root alias");
|
||||
symlink("procfs-alias", &chained_alias).expect("chained alias");
|
||||
|
||||
let mut procfs_paths = vec![
|
||||
PathBuf::from("/proc/self/cwd/missing-config"),
|
||||
PathBuf::from("/proc/thread-self/cwd/missing-config"),
|
||||
PathBuf::from("/proc/self/fd/1048575/missing-config"),
|
||||
PathBuf::from("/proc/self/exe"),
|
||||
PathBuf::from(format!("/proc/{}/cwd/missing-config", std::process::id())),
|
||||
PathBuf::from("/proc/4294967295/cwd/missing-config"),
|
||||
procfs_alias.join("missing-config"),
|
||||
procfs_root_alias.join("4294967295/cwd/missing-config"),
|
||||
chained_alias.join("missing-config"),
|
||||
];
|
||||
if Path::new("/dev/fd").exists() {
|
||||
procfs_paths.push(PathBuf::from("/dev/fd/1048575/missing-config"));
|
||||
}
|
||||
for path in procfs_paths {
|
||||
assert!(
|
||||
route_contains_process_relative_procfs_path(&path).expect("inspect procfs route"),
|
||||
"expected process-relative procfs route in {}",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
|
||||
for path in [
|
||||
PathBuf::from("/proc/version"),
|
||||
ordinary_alias.join("missing-config"),
|
||||
literal,
|
||||
] {
|
||||
assert!(
|
||||
!route_contains_process_relative_procfs_path(&path).expect("inspect ordinary route"),
|
||||
"unexpected process-relative procfs route in {}",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user