Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix TestContainerNoBinaryExists test, by making create behaviour similar to runc #1347

Merged
merged 3 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions crates/libcontainer/src/process/container_init_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,32 @@ pub fn container_init_process(
}
}

// this checks if the binary to run actually exists and if we have permissions to run it.
// Taken from https://github.com/opencontainers/runc/blob/25c9e888686773e7e06429133578038a9abc091d/libcontainer/standard_init_linux.go#L195-L206
if let Some(args) = proc.args() {
let path_var = {
let mut ret: &str = "";
for var in &envs {
if var.starts_with("PATH=") {
ret = var;
}
}
ret
};
let executable_path = utils::get_executable_path(&args[0], path_var);
match executable_path {
None => bail!(
"executable '{}' for container process does not exist",
args[0]
),
Some(path) => {
if !utils::is_executable(&path)? {
bail!("file {:?} does not have executable permission set", path);
}
}
}
}

// Notify main process that the init process is ready to execute the
// payload. Note, because we are already inside the pid namespace, the pid
// outside the pid namespace should be recorded by the intermediate process
Expand Down
61 changes: 61 additions & 0 deletions crates/libcontainer/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,31 @@ pub fn get_temp_dir_path(test_name: &str) -> PathBuf {
std::env::temp_dir().join(test_name)
}

pub fn get_executable_path(name: &str, path_var: &str) -> Option<PathBuf> {
YJDoc2 marked this conversation as resolved.
Show resolved Hide resolved
let paths = path_var.trim_start_matches("PATH=");
// if path has / in it, we have to assume absolute path, as per runc impl
if name.contains('/') && PathBuf::from(name).exists() {
return Some(PathBuf::from(name));
}
for path in paths.split(':') {
let potential_path = PathBuf::from(path).join(name);
if potential_path.exists() {
return Some(potential_path);
}
}
None
}

pub fn is_executable(path: &Path) -> Result<bool> {
use std::os::unix::fs::PermissionsExt;
let metadata = path.metadata()?;
let permissions = metadata.permissions();
// we have to check if the path is file and the execute bit
// is set. In case of directories, the execute bit is also set,
// so have to check if this is a file or not
Ok(metadata.is_file() && permissions.mode() & 0o001 != 0)
}

#[cfg(test)]
pub(crate) mod test_utils {
use crate::process::channel;
Expand Down Expand Up @@ -505,4 +530,40 @@ mod tests {
PathBuf::from(&test_root_dir).join("somepath/passwd")
);
}

#[test]
fn test_get_executable_path() {
let non_existing_abs_path = "/some/non/existent/absolute/path";
let existing_abs_path = "/usr/bin/sh";
let existing_binary = "sh";
let non_existing_binary = "non-existent";
let path_value = "PATH=/usr/bin:/bin";

assert_eq!(
get_executable_path(existing_abs_path, path_value),
Some(PathBuf::from(existing_abs_path))
);
assert_eq!(get_executable_path(non_existing_abs_path, path_value), None);

assert_eq!(
get_executable_path(existing_binary, path_value),
Some(PathBuf::from("/usr/bin/sh"))
);

assert_eq!(get_executable_path(non_existing_binary, path_value), None);
}

#[test]
fn test_is_executable() {
let executable_path = PathBuf::from("/bin/sh");
let directory_path = PathBuf::from("/tmp");
// a file guaranteed to be on linux and not executable
let non_executable_path = PathBuf::from("/boot/initrd.img");
let non_existent_path = PathBuf::from("/some/non/existent/path");

assert!(is_executable(&non_existent_path).is_err());
assert!(is_executable(&executable_path).unwrap());
assert!(!is_executable(&non_executable_path).unwrap());
assert!(!is_executable(&directory_path).unwrap());
}
}
2 changes: 1 addition & 1 deletion scripts/oci_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ test_cases=(
"linux_seccomp/linux_seccomp.t"
"linux_sysctl/linux_sysctl.t"
"linux_uid_mappings/linux_uid_mappings.t"
"misc_props/misc_props.t"
# "misc_props/misc_props.t" runc also fails this, check out https://github.com/containers/youki/pull/1347#issuecomment-1315332775
"mounts/mounts.t"
# This test case passed on local box, but not on Github Action. `runc` also fails on Github Action, so likely it is an issue with the test.
# "pidfile/pidfile.t"
Expand Down