mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-24 17:05:47 +00:00
PVF worker: Add seccomp restrictions (restrict networking) (#2009)
This commit is contained in:
@@ -28,7 +28,7 @@ async fn execute_good_block_on_parent() {
|
||||
|
||||
let block_data = BlockData { state: 0, add: 512 };
|
||||
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
|
||||
let ret = host
|
||||
.validate_candidate(
|
||||
@@ -56,7 +56,7 @@ async fn execute_good_chain_on_parent() {
|
||||
let mut parent_hash = [0; 32];
|
||||
let mut last_state = 0;
|
||||
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
|
||||
for (number, add) in (0..10).enumerate() {
|
||||
let parent_head =
|
||||
@@ -98,7 +98,7 @@ async fn execute_bad_block_on_parent() {
|
||||
add: 256,
|
||||
};
|
||||
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
|
||||
let _err = host
|
||||
.validate_candidate(
|
||||
@@ -117,7 +117,7 @@ async fn execute_bad_block_on_parent() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn stress_spawn() {
|
||||
let host = std::sync::Arc::new(TestHost::new());
|
||||
let host = std::sync::Arc::new(TestHost::new().await);
|
||||
|
||||
async fn execute(host: std::sync::Arc<TestHost>) {
|
||||
let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) };
|
||||
@@ -149,9 +149,12 @@ async fn stress_spawn() {
|
||||
// With one worker, run multiple execution jobs serially. They should not conflict.
|
||||
#[tokio::test]
|
||||
async fn execute_can_run_serially() {
|
||||
let host = std::sync::Arc::new(TestHost::new_with_config(|cfg| {
|
||||
cfg.execute_workers_max_num = 1;
|
||||
}));
|
||||
let host = std::sync::Arc::new(
|
||||
TestHost::new_with_config(|cfg| {
|
||||
cfg.execute_workers_max_num = 1;
|
||||
})
|
||||
.await,
|
||||
);
|
||||
|
||||
async fn execute(host: std::sync::Arc<TestHost>) {
|
||||
let parent_head = HeadData { number: 0, parent_hash: [0; 32], post_state: hash_state(0) };
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[cfg(feature = "ci-only-tests")]
|
||||
use assert_matches::assert_matches;
|
||||
use parity_scale_codec::Encode as _;
|
||||
use polkadot_node_core_pvf::{
|
||||
@@ -24,6 +23,8 @@ use polkadot_node_core_pvf::{
|
||||
};
|
||||
use polkadot_parachain_primitives::primitives::{BlockData, ValidationParams, ValidationResult};
|
||||
use polkadot_primitives::ExecutorParams;
|
||||
#[cfg(target_os = "linux")]
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
#[cfg(feature = "ci-only-tests")]
|
||||
use polkadot_primitives::ExecutorParam;
|
||||
@@ -43,11 +44,11 @@ struct TestHost {
|
||||
}
|
||||
|
||||
impl TestHost {
|
||||
fn new() -> Self {
|
||||
Self::new_with_config(|_| ())
|
||||
async fn new() -> Self {
|
||||
Self::new_with_config(|_| ()).await
|
||||
}
|
||||
|
||||
fn new_with_config<F>(f: F) -> Self
|
||||
async fn new_with_config<F>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce(&mut Config),
|
||||
{
|
||||
@@ -61,7 +62,7 @@ impl TestHost {
|
||||
execute_worker_path,
|
||||
);
|
||||
f(&mut config);
|
||||
let (host, task) = start(config, Metrics::default());
|
||||
let (host, task) = start(config, Metrics::default()).await;
|
||||
let _ = tokio::task::spawn(task);
|
||||
Self { cache_dir, host: Mutex::new(host) }
|
||||
}
|
||||
@@ -127,7 +128,7 @@ impl TestHost {
|
||||
|
||||
#[tokio::test]
|
||||
async fn terminates_on_timeout() {
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let result = host
|
||||
@@ -153,11 +154,113 @@ async fn terminates_on_timeout() {
|
||||
assert!(duration < TEST_EXECUTION_TIMEOUT * JOB_TIMEOUT_WALL_CLOCK_FACTOR);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn kill_by_sid_and_name(sid: i32, exe_name: &'static str) {
|
||||
use procfs::process;
|
||||
|
||||
let all_processes: Vec<process::Process> = process::all_processes()
|
||||
.expect("Can't read /proc")
|
||||
.filter_map(|p| match p {
|
||||
Ok(p) => Some(p), // happy path
|
||||
Err(e) => match e {
|
||||
// process vanished during iteration, ignore it
|
||||
procfs::ProcError::NotFound(_) => None,
|
||||
x => {
|
||||
panic!("some unknown error: {}", x);
|
||||
},
|
||||
},
|
||||
})
|
||||
.collect();
|
||||
|
||||
for process in all_processes {
|
||||
if process.stat().unwrap().session == sid &&
|
||||
process.exe().unwrap().to_str().unwrap().contains(exe_name)
|
||||
{
|
||||
assert_eq!(unsafe { libc::kill(process.pid(), 9) }, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run these tests in their own processes with rusty-fork. They work by each creating a new session,
|
||||
// then killing the worker process that matches the session ID and expected worker name.
|
||||
#[cfg(target_os = "linux")]
|
||||
rusty_fork_test! {
|
||||
// What happens when the prepare worker dies in the middle of a job?
|
||||
#[test]
|
||||
fn prepare_worker_killed_during_job() {
|
||||
const PROCESS_NAME: &'static str = "polkadot-prepare-worker";
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let host = TestHost::new().await;
|
||||
|
||||
// Create a new session and get the session ID.
|
||||
let sid = unsafe { libc::setsid() };
|
||||
assert!(sid > 0);
|
||||
|
||||
let (result, _) = futures::join!(
|
||||
// Choose a job that would normally take the entire timeout.
|
||||
host.precheck_pvf(rococo_runtime::WASM_BINARY.unwrap(), Default::default()),
|
||||
// Run a future that kills the job in the middle of the timeout.
|
||||
async {
|
||||
tokio::time::sleep(TEST_PREPARATION_TIMEOUT / 2).await;
|
||||
kill_by_sid_and_name(sid, PROCESS_NAME);
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(result, Err(PrepareError::IoErr(_)));
|
||||
})
|
||||
}
|
||||
|
||||
// What happens when the execute worker dies in the middle of a job?
|
||||
#[test]
|
||||
fn execute_worker_killed_during_job() {
|
||||
const PROCESS_NAME: &'static str = "polkadot-execute-worker";
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let host = TestHost::new().await;
|
||||
|
||||
// Create a new session and get the session ID.
|
||||
let sid = unsafe { libc::setsid() };
|
||||
assert!(sid > 0);
|
||||
|
||||
// Prepare the artifact ahead of time.
|
||||
let binary = halt::wasm_binary_unwrap();
|
||||
host.precheck_pvf(binary, Default::default()).await.unwrap();
|
||||
|
||||
let (result, _) = futures::join!(
|
||||
// Choose an job that would normally take the entire timeout.
|
||||
host.validate_candidate(
|
||||
binary,
|
||||
ValidationParams {
|
||||
block_data: BlockData(Vec::new()),
|
||||
parent_head: Default::default(),
|
||||
relay_parent_number: 1,
|
||||
relay_parent_storage_root: Default::default(),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
// Run a future that kills the job in the middle of the timeout.
|
||||
async {
|
||||
tokio::time::sleep(TEST_EXECUTION_TIMEOUT / 2).await;
|
||||
kill_by_sid_and_name(sid, PROCESS_NAME);
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
result,
|
||||
Err(ValidationError::InvalidCandidate(InvalidCandidate::AmbiguousWorkerDeath))
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ci-only-tests")]
|
||||
#[tokio::test]
|
||||
async fn ensure_parallel_execution() {
|
||||
// Run some jobs that do not complete, thus timing out.
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
let execute_pvf_future_1 = host.validate_candidate(
|
||||
halt::wasm_binary_unwrap(),
|
||||
ValidationParams {
|
||||
@@ -204,7 +307,8 @@ async fn ensure_parallel_execution() {
|
||||
async fn execute_queue_doesnt_stall_if_workers_died() {
|
||||
let host = TestHost::new_with_config(|cfg| {
|
||||
cfg.execute_workers_max_num = 5;
|
||||
});
|
||||
})
|
||||
.await;
|
||||
|
||||
// Here we spawn 8 validation jobs for the `halt` PVF and share those between 5 workers. The
|
||||
// first five jobs should timeout and the workers killed. For the next 3 jobs a new batch of
|
||||
@@ -241,7 +345,8 @@ async fn execute_queue_doesnt_stall_if_workers_died() {
|
||||
async fn execute_queue_doesnt_stall_with_varying_executor_params() {
|
||||
let host = TestHost::new_with_config(|cfg| {
|
||||
cfg.execute_workers_max_num = 2;
|
||||
});
|
||||
})
|
||||
.await;
|
||||
|
||||
let executor_params_1 = ExecutorParams::default();
|
||||
let executor_params_2 = ExecutorParams::from(&[ExecutorParam::StackLogicalMax(1024)][..]);
|
||||
@@ -289,7 +394,7 @@ async fn execute_queue_doesnt_stall_with_varying_executor_params() {
|
||||
// Test that deleting a prepared artifact does not lead to a dispute when we try to execute it.
|
||||
#[tokio::test]
|
||||
async fn deleting_prepared_artifact_does_not_dispute() {
|
||||
let host = TestHost::new();
|
||||
let host = TestHost::new().await;
|
||||
let cache_dir = host.cache_dir.path();
|
||||
|
||||
let _stats = host.precheck_pvf(halt::wasm_binary_unwrap(), Default::default()).await.unwrap();
|
||||
@@ -334,7 +439,8 @@ async fn deleting_prepared_artifact_does_not_dispute() {
|
||||
async fn prepare_can_run_serially() {
|
||||
let host = TestHost::new_with_config(|cfg| {
|
||||
cfg.prepare_workers_hard_max_num = 1;
|
||||
});
|
||||
})
|
||||
.await;
|
||||
|
||||
let _stats = host
|
||||
.precheck_pvf(::adder::wasm_binary_unwrap(), Default::default())
|
||||
|
||||
Reference in New Issue
Block a user