This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
PVF: Move landlock out of thread into process; add landlock exceptions #7580
Draft
mrcnski
wants to merge
16
commits into
master
Choose a base branch
from
mrcnski/pvf-sandbox-whole-process
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
3cc75d0
Move landlock out of thread into process; add landlock exceptions
mrcnski 1af053c
Merge branch 'master' into mrcnski/pvf-sandbox-whole-process
mrcnski f34cec4
Add --cache-path cli param
mrcnski d416b57
Finishing touches
mrcnski f1e4381
Remove unneeded argument in test
mrcnski 49f4982
Fix compile error!
mrcnski 4fe842a
Remove some unnecessary unwraps
mrcnski 6d9c276
Add note about `extra_args`
mrcnski 69d15b4
Landlock: disallow listing directories
mrcnski d3231bd
Merge branch 'master' into mrcnski/pvf-sandbox-whole-process
mrcnski c5879b2
Only throw an error if landlock is enabled and expected to work
mrcnski 99761eb
Update Cargo.lock
mrcnski 75ff594
Update a docstring
mrcnski e00d0b4
Change root to be the artifact directory.
mrcnski 8807302
Merge branch 'master' into mrcnski/pvf-sandbox-whole-process
mrcnski cd41603
Experiment with pivot_root in child workers
mrcnski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,7 +23,7 @@ use cpu_time::ProcessTime; | |
use futures::never::Never; | ||
use std::{ | ||
any::Any, | ||
path::PathBuf, | ||
path::{Path, PathBuf}, | ||
sync::mpsc::{Receiver, RecvTimeoutError}, | ||
time::Duration, | ||
}; | ||
|
@@ -70,17 +70,23 @@ macro_rules! decl_worker_main { | |
} | ||
|
||
let mut node_version = None; | ||
let mut socket_path: &str = ""; | ||
let mut socket_path = None; | ||
let mut cache_path = None; | ||
|
||
for i in (2..args.len()).step_by(2) { | ||
match args[i].as_ref() { | ||
"--socket-path" => socket_path = args[i + 1].as_str(), | ||
"--socket-path" => socket_path = Some(args[i + 1].as_str()), | ||
"--node-impl-version" => node_version = Some(args[i + 1].as_str()), | ||
"--cache-path" => cache_path = Some(args[i + 1].as_str()), | ||
arg => panic!("Unexpected argument found: {}", arg), | ||
} | ||
} | ||
let socket_path = socket_path.expect("the --socket-path argument is required"); | ||
let cache_path = cache_path.expect("the --cache-path argument is required"); | ||
|
||
$entrypoint(&socket_path, node_version, Some($worker_version)); | ||
let cache_path = &std::path::Path::new(cache_path); | ||
|
||
$entrypoint(&socket_path, node_version, Some($worker_version), cache_path); | ||
} | ||
}; | ||
} | ||
|
@@ -102,6 +108,7 @@ pub fn worker_event_loop<F, Fut>( | |
socket_path: &str, | ||
node_version: Option<&str>, | ||
worker_version: Option<&str>, | ||
cache_path: &Path, | ||
mut event_loop: F, | ||
) where | ||
F: FnMut(UnixStream) -> Fut, | ||
|
@@ -115,6 +122,7 @@ pub fn worker_event_loop<F, Fut>( | |
if node_version != worker_version { | ||
gum::error!( | ||
target: LOG_TARGET, | ||
%debug_id, | ||
%worker_pid, | ||
%node_version, | ||
%worker_version, | ||
|
@@ -127,8 +135,28 @@ pub fn worker_event_loop<F, Fut>( | |
} | ||
} | ||
|
||
#[cfg(target_os = "linux")] | ||
{ | ||
if let Err(err_ctx) = change_root(cache_path) { | ||
let err = io::Error::last_os_error(); | ||
gum::error!( | ||
target: LOG_TARGET, | ||
%debug_id, | ||
%worker_pid, | ||
%err_ctx, | ||
?cache_path, | ||
"Could not change root to be the cache path: {}", | ||
err | ||
); | ||
worker_shutdown_message(debug_id, worker_pid, err); | ||
return | ||
} | ||
} | ||
|
||
remove_env_vars(debug_id); | ||
|
||
gum::info!(target: LOG_TARGET, "5. {:?}", std::fs::read_dir(".").unwrap().map(|entry| entry.unwrap().path()).collect::<Vec<PathBuf>>()); | ||
|
||
// Run the main worker loop. | ||
let rt = Runtime::new().expect("Creates tokio runtime. If this panics the worker will die and the host will detect that and deal with it."); | ||
let err = rt | ||
|
@@ -151,6 +179,87 @@ pub fn worker_event_loop<F, Fut>( | |
rt.shutdown_background(); | ||
} | ||
|
||
/// Change root to be the artifact directory. | ||
#[cfg(target_os = "linux")] | ||
fn change_root(cache_path: &Path) -> Result<(), &'static str> { | ||
use rand::{distributions::Alphanumeric, Rng}; | ||
use std::{ffi::CString, os::unix::ffi::OsStrExt, ptr}; | ||
|
||
const RANDOM_LEN: usize = 10; | ||
let mut buf = Vec::with_capacity(RANDOM_LEN); | ||
buf.extend(rand::thread_rng().sample_iter(&Alphanumeric).take(RANDOM_LEN)); | ||
let s = std::str::from_utf8(&buf) | ||
.expect("the string is collected from a valid utf-8 sequence; qed"); | ||
|
||
let cache_path_str = match cache_path.to_str() { | ||
Some(s) => s, | ||
None => return Err("cache path is not valid UTF-8") | ||
}; | ||
mrcnski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let cache_path_c = CString::new(cache_path.as_os_str().as_bytes()).unwrap(); | ||
let root_absolute_c = CString::new("/").unwrap(); | ||
// Append a random string to prevent races and to avoid dealing with the dir already existing. | ||
let oldroot_relative_c = CString::new(format!("{}/oldroot-{}", cache_path_str, s)).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
let oldroot_absolute_c = CString::new(format!("/oldroot-{}", s)).unwrap(); | ||
|
||
// SAFETY: TODO | ||
unsafe { | ||
// 1. `unshare` the user and the mount namespaces. | ||
if libc::unshare(libc::CLONE_NEWUSER) < 0 { | ||
return Err("unshare user namespace") | ||
} | ||
if libc::unshare(libc::CLONE_NEWNS) < 0 { | ||
return Err("unshare mount namespace") | ||
} | ||
Comment on lines
+207
to
+212
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
// 2. `pivot_root` to the artifact directory. | ||
gum::info!(target: LOG_TARGET, "1. {:?}", std::env::current_dir()); | ||
gum::info!(target: LOG_TARGET, "1.5. {:?}", std::fs::read_dir(".").unwrap().map(|entry| entry.unwrap().path()).collect::<Vec<PathBuf>>()); | ||
// TODO: Ensure that 'new_root' and its parent mount don't have shared propagation. | ||
if libc::mount(ptr::null(), root_absolute_c.as_ptr(), ptr::null(), libc::MS_REC | libc::MS_PRIVATE, ptr::null()) < 0 { | ||
return Err("mount MS_PRIVATE") | ||
} | ||
if libc::mount( | ||
cache_path_c.as_ptr(), | ||
cache_path_c.as_ptr(), | ||
ptr::null(), // ignored when MS_BIND is used | ||
libc::MS_BIND | libc::MS_REC | libc::MS_NOEXEC, | ||
ptr::null(), // ignored when MS_BIND is used | ||
) < 0 | ||
{ | ||
return Err("mount MS_BIND") | ||
} | ||
if libc::mkdir(oldroot_relative_c.as_ptr(), 0755) < 0 { | ||
return Err("mkdir oldroot") | ||
} | ||
if libc::syscall( | ||
libc::SYS_pivot_root, | ||
cache_path_c.as_ptr(), | ||
oldroot_relative_c.as_ptr(), | ||
) < 0 | ||
{ | ||
return Err("pivot_root") | ||
} | ||
Comment on lines
+231
to
+241
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you call the pivot root syscall with both paths being |
||
|
||
// 3. Change to the new root, `unmount2` and remove the old root. | ||
if libc::chdir(root_absolute_c.as_ptr()) < 0 { | ||
return Err("chdir to new root") | ||
} | ||
gum::info!(target: LOG_TARGET, "2. {:?}", std::env::current_dir()); | ||
gum::info!(target: LOG_TARGET, "3. {:?}", std::fs::read_dir(".").unwrap().map(|entry| entry.unwrap().path()).collect::<Vec<PathBuf>>()); | ||
if libc::umount2(oldroot_absolute_c.as_ptr(), libc::MNT_DETACH) < 0 { | ||
return Err("umount2 the oldroot") | ||
} | ||
if libc::rmdir(oldroot_absolute_c.as_ptr()) < 0 { | ||
return Err("rmdir the oldroot") | ||
} | ||
gum::info!(target: LOG_TARGET, "4. {:?}", std::fs::read_dir(".").unwrap().map(|entry| entry.unwrap().path()).collect::<Vec<PathBuf>>()); | ||
|
||
// TODO: do some assertions | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Delete all env vars to prevent malicious code from accessing them. | ||
fn remove_env_vars(debug_id: &'static str) { | ||
for (key, value) in std::env::vars_os() { | ||
|
@@ -299,7 +408,7 @@ pub mod thread { | |
Arc::new((Mutex::new(WaitOutcome::Pending), Condvar::new())) | ||
} | ||
|
||
/// Runs a worker thread. Will first enable security features, and afterwards notify the threads | ||
/// Runs a worker thread. Will run the requested function, and afterwards notify the threads | ||
/// waiting on the condvar. Catches panics during execution and resumes the panics after | ||
/// triggering the condvar, so that the waiting thread is notified on panics. | ||
/// | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to do a
.collect
here directly into the buffer.