From 6c8b46795867f791a5956de1741965d032f6d21f Mon Sep 17 00:00:00 2001 From: Tiffany Bennett Date: Tue, 4 Oct 2016 20:22:52 -0400 Subject: [PATCH] Use a custom sandbox to avoid OOM --- web/Cargo.toml | 2 + web/src/main.rs | 18 ++++++++- web/src/worker.rs | 88 +++++++++++++++++++++++++++++++++++++++++ web/templates/index.hbs | 4 +- 4 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 web/src/worker.rs diff --git a/web/Cargo.toml b/web/Cargo.toml index bced0cc..e8b198f 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -23,3 +23,5 @@ handlebars = "0.20.0" handlebars-iron = "0.18.0" staticfile = "0.3.0" mount = "0.2.1" +ipc-channel = "0.5.1" +libc = "0.2.14" diff --git a/web/src/main.rs b/web/src/main.rs index aad2b09..f2e27d7 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -10,6 +10,10 @@ extern crate handlebars; extern crate handlebars_iron; extern crate staticfile; extern crate mount; +extern crate ipc_channel; +extern crate libc; + +pub mod worker; use iron::prelude::*; use iron::status; @@ -21,6 +25,8 @@ use mount::Mount; use staticfile::Static; use std::collections::BTreeMap; use params::{Params, Value}; +use std::env; +use worker::eval; fn root(req: &mut Request) -> IronResult { let mut data = BTreeMap::new(); @@ -28,7 +34,7 @@ fn root(req: &mut Request) -> IronResult { let map = req.get_ref::().unwrap(); match map.find(&["q"]) { Some(&Value::String(ref query)) => { - let reply = rink::one_line_sandbox(query); + let reply = eval(query); data.insert("content".to_owned(), reply); }, _ => (), @@ -46,12 +52,20 @@ fn api(req: &mut Request) -> IronResult { _ => return Ok(Response::with((acao, status::BadRequest))), }; - let reply = rink::one_line_sandbox(query); + let reply = eval(query); Ok(Response::with((acao, status::Ok, reply))) } fn main() { + let mut args = env::args(); + args.next(); + if args.next().map(|x| x == "--sandbox").unwrap_or(false) { + let server = args.next().unwrap(); + let query = args.next().unwrap(); + worker::worker(&server, &query); + } + let mut mount = Mount::new(); let mut router = Router::new(); diff --git a/web/src/worker.rs b/web/src/worker.rs new file mode 100644 index 0000000..b5421da --- /dev/null +++ b/web/src/worker.rs @@ -0,0 +1,88 @@ +use libc; +use std::io::Error; +use ipc_channel::ipc::{IpcOneShotServer, IpcSender}; +use std::process::{Command, Stdio}; +use std::env; +use rink; +use std::os::unix::process::ExitStatusExt; + +pub fn worker(server_name: &str, query: &str) -> ! { + let tx = IpcSender::connect(server_name.to_owned()).unwrap(); + + tx.send("".to_owned()).unwrap(); + + unsafe { + let limit = libc::rlimit { + // 100 megabytes + rlim_cur: 100_000_000, + rlim_max: 100_000_000, + }; + let res = libc::setrlimit(libc::RLIMIT_AS, &limit); + if res == -1 { + panic!("Setrlimit RLIMIT_AS failed: {}", Error::last_os_error()) + } + let limit = libc::rlimit { + // 15 seconds + rlim_cur: 15, + rlim_max: 15 + }; + let res = libc::setrlimit(libc::RLIMIT_CPU, &limit); + if res == -1 { + panic!("Setrlimit RLIMIT_AS failed: {}", Error::last_os_error()) + } + } + + let mut ctx = rink::load().unwrap(); + ctx.short_output = true; + let reply = match rink::one_line(&mut ctx, query) { + Ok(v) => v, + Err(e) => e + }; + tx.send(reply).unwrap(); + + ::std::process::exit(0) +} + +pub fn eval(query: &str) -> String { + let (server, server_name) = IpcOneShotServer::new().unwrap(); + + let res = Command::new(env::current_exe().unwrap()) + .arg("--sandbox") + .arg(server_name) + .arg(query) + .stdin(Stdio::null()) + .stderr(Stdio::piped()) + .stdout(Stdio::piped()) + .env("RUST_BACKTRACE", "1") + .spawn() + .map_err(|x| format!("{}", x)); + let child = match res { + Ok(s) => s, + Err(e) => return format!("Failed to run sandbox: {}", e) + }; + let (rx, _) = server.accept().unwrap(); + + match rx.recv() { + Ok(s) => s, + Err(e) => { + let output = match child.wait_with_output() { + Ok(v) => v, + Err(e) => return format!("{}", e) + }; + match output.status.signal() { + Some(libc::SIGXCPU) => return format!("Calculation went over time limit"), + _ => () + }; + format!( + "Receiving reply from sandbox failed: {}\n\ + Signal: {}\n\ + Sandbox stdout: {}\n\ + Sandbox stderr: {}", + e, + output.status.signal().map(|x| format!("{}", x)).unwrap_or("None".to_owned()), + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ) + } + } +} diff --git a/web/templates/index.hbs b/web/templates/index.hbs index 242fa96..f76fb73 100644 --- a/web/templates/index.hbs +++ b/web/templates/index.hbs @@ -41,7 +41,9 @@
- {{content}} +
+        {{content}}
+