Add resource-checked extraction worker and bounded parent process owner
This commit is contained in:
1 parent
688b5dc519
commit
844fdbf89f
11 files changed
+849
-3
No files matched your search
@@ -0,0 +1,40 @@
|
||||
//! Single-request, path-free document parser helper. Parent owns deadline,
|
||||
//! cancellation and Windows Job lifetime; this helper never spawns children.
|
||||
mod resources;
|
||||
use anyhow::{ensure, Result};
|
||||
use searchi_core::{extraction_protocol::extract_document, extraction_transport};
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
fn run() -> Result<()> {
|
||||
ensure!(std::env::args_os().len() == 1, "Arguments are unsupported");
|
||||
let mut input = io::stdin().lock();
|
||||
#[cfg(unix)]
|
||||
resources::apply()?;
|
||||
// Prefix arrival synchronizes with Windows parent job assignment. No
|
||||
// attacker-declared allocation or parser runs before the policy check.
|
||||
let mut prefix = [0u8; 4];
|
||||
input.read_exact(&mut prefix)?;
|
||||
#[cfg(not(unix))]
|
||||
resources::apply()?;
|
||||
let mut input = io::Cursor::new(prefix).chain(&mut input);
|
||||
let (request, bytes) = extraction_transport::read_request(&mut input)?;
|
||||
// Parent closes stdin after the single frame. Extra data never reaches a parser.
|
||||
let mut trailing = [0u8; 1];
|
||||
ensure!(
|
||||
input.read(&mut trailing)? == 0,
|
||||
"Trailing input is unsupported"
|
||||
);
|
||||
let response = extract_document(&request, &bytes)?;
|
||||
let mut output = io::stdout().lock();
|
||||
extraction_transport::write_response(&mut output, &response, &request)?;
|
||||
output.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if run().is_err() {
|
||||
// Do not echo document contents, external paths or untrusted protocol values.
|
||||
eprintln!("searchi extraction worker rejected request or resource policy");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use anyhow::{ensure, Result};
|
||||
|
||||
const MEMORY_BYTES: usize = 512 * 1024 * 1024;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn apply() -> Result<()> {
|
||||
use windows_sys::Win32::System::JobObjects::{
|
||||
JobObjectExtendedLimitInformation, QueryInformationJobObject,
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
|
||||
JOB_OBJECT_LIMIT_PROCESS_MEMORY,
|
||||
};
|
||||
let mut information: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { std::mem::zeroed() };
|
||||
// NULL addresses the job associated with the current process. The parent
|
||||
// must assign this process before writing its first input bytes.
|
||||
let success = unsafe {
|
||||
QueryInformationJobObject(
|
||||
std::ptr::null_mut(),
|
||||
JobObjectExtendedLimitInformation,
|
||||
&mut information as *mut _ as *mut _,
|
||||
std::mem::size_of_val(&information) as u32,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
ensure!(
|
||||
success != 0,
|
||||
"Extraction worker requires parent resource job"
|
||||
);
|
||||
let required = JOB_OBJECT_LIMIT_PROCESS_MEMORY | JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
ensure!(
|
||||
information.BasicLimitInformation.LimitFlags & required == required
|
||||
&& information.ProcessMemoryLimit > 0
|
||||
&& information.ProcessMemoryLimit <= MEMORY_BYTES,
|
||||
"Extraction worker job resource policy is missing"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn apply() -> Result<()> {
|
||||
for (resource, maximum) in [
|
||||
(libc::RLIMIT_AS, MEMORY_BYTES as libc::rlim_t),
|
||||
(libc::RLIMIT_CPU, 30 as libc::rlim_t),
|
||||
] {
|
||||
let value = libc::rlimit {
|
||||
rlim_cur: maximum,
|
||||
rlim_max: maximum,
|
||||
};
|
||||
// Applied before allocating input or invoking any parser. Platform
|
||||
// enforcement semantics, especially macOS, require native evidence.
|
||||
ensure!(
|
||||
unsafe { libc::setrlimit(resource, &value) } == 0,
|
||||
"Cannot install extraction resource limit"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows, unix)))]
|
||||
pub fn apply() -> Result<()> {
|
||||
anyhow::bail!("Extraction resource policy unsupported on this platform")
|
||||
}
|
||||
Reference in new issue
Block a user