Skip to content

Commit 5b58c63

Browse files
committed
jailer: make resource limits configurable
Signed-off-by: Luminita Voicu <[email protected]>
1 parent c4bc460 commit 5b58c63

File tree

3 files changed

+143
-3
lines changed

3 files changed

+143
-3
lines changed

src/jailer/src/env.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::process::{Command, Stdio};
1212
use crate::cgroup;
1313
use crate::cgroup::Cgroup;
1414
use crate::chroot::chroot;
15-
use crate::resource_limits::ResourceLimits;
15+
use crate::resource_limits::{ResourceLimits, FSIZE_ARG, NO_FILE_ARG};
1616
use crate::{Error, Result};
1717
use std::io;
1818
use std::io::Write;
@@ -169,7 +169,10 @@ impl Env {
169169
}
170170
}
171171

172-
let resource_limits = ResourceLimits::default();
172+
let mut resource_limits = ResourceLimits::default();
173+
if let Some(args) = arguments.multiple_values("resource-limit") {
174+
Env::parse_resource_limits(&mut resource_limits, args)?;
175+
}
173176

174177
Ok(Env {
175178
id: id.to_owned(),
@@ -201,6 +204,24 @@ impl Env {
201204
self.uid
202205
}
203206

207+
fn parse_resource_limits(resource_limits: &mut ResourceLimits, args: &[String]) -> Result<()> {
208+
for arg in args {
209+
let (name, value) = arg
210+
.split_once('=')
211+
.ok_or_else(|| Error::ResLimitFormat(arg.to_string()))?;
212+
213+
let limit_value = value
214+
.parse::<u64>()
215+
.map_err(|err| Error::ResLimitValue(value.to_string(), err.to_string()))?;
216+
match name {
217+
FSIZE_ARG => resource_limits.set_file_size(limit_value),
218+
NO_FILE_ARG => resource_limits.set_no_file(limit_value),
219+
_ => return Err(Error::ResLimitArgument(name.to_string())),
220+
}
221+
}
222+
Ok(())
223+
}
224+
204225
fn exec_into_new_pid_ns(&mut self, chroot_exec_file: PathBuf) -> Result<()> {
205226
// Unshare into a new PID namespace.
206227
// The current process will not be moved into the newly created namespace, but its first
@@ -562,6 +583,7 @@ mod tests {
562583
pub daemonize: bool,
563584
pub new_pid_ns: bool,
564585
pub cgroups: Vec<&'a str>,
586+
pub resource_limits: Vec<&'a str>,
565587
}
566588

567589
impl ArgVals<'_> {
@@ -577,6 +599,7 @@ mod tests {
577599
daemonize: true,
578600
new_pid_ns: true,
579601
cgroups: vec!["cpu.shares=2", "cpuset.mems=0"],
602+
resource_limits: vec!["no-file=1024", "fsize=1048575"],
580603
}
581604
}
582605
}
@@ -607,6 +630,12 @@ mod tests {
607630
arg_vec.push((*cg).to_string());
608631
}
609632

633+
// Append limits arguments
634+
for limit in &arg_vals.resource_limits {
635+
arg_vec.push("--resource-limit".to_string());
636+
arg_vec.push((*limit).to_string());
637+
}
638+
610639
if let Some(s) = arg_vals.netns {
611640
arg_vec.push("--netns".to_string());
612641
arg_vec.push(s.to_string());
@@ -702,6 +731,16 @@ mod tests {
702731
args.parse(&make_args(&invalid_cgroup_arg_vals)).unwrap();
703732
assert!(Env::new(&args, 0, 0).is_err());
704733

734+
let invalid_res_limit_arg_vals = ArgVals {
735+
resource_limits: vec!["zzz"],
736+
..base_invalid_arg_vals.clone()
737+
};
738+
739+
let arg_parser = build_arg_parser();
740+
args = arg_parser.arguments().clone();
741+
args.parse(&make_args(&invalid_res_limit_arg_vals)).unwrap();
742+
assert!(Env::new(&args, 0, 0).is_err());
743+
705744
let invalid_id_arg_vals = ArgVals {
706745
id: "/ad./sa12",
707746
..base_invalid_arg_vals.clone()
@@ -884,6 +923,7 @@ mod tests {
884923
daemonize: false,
885924
new_pid_ns: false,
886925
cgroups: Vec::new(),
926+
resource_limits: Vec::new(),
887927
};
888928
fs::write(some_file_path, "some_content").unwrap();
889929
args.parse(&make_args(&some_arg_vals)).unwrap();
@@ -1006,6 +1046,71 @@ mod tests {
10061046
assert!(Env::new(&args, 0, 0).is_ok());
10071047
}
10081048

1049+
#[test]
1050+
fn test_parse_resource_limits() {
1051+
let mut resource_limits = ResourceLimits::default();
1052+
1053+
// Cases that should fail
1054+
1055+
// Check invalid formats
1056+
let invalid_formats = ["", "foo"];
1057+
for format in invalid_formats.iter() {
1058+
let arg = vec![format.to_string()];
1059+
assert_eq!(
1060+
format!(
1061+
"{:?}",
1062+
Env::parse_resource_limits(&mut resource_limits, &*arg)
1063+
.err()
1064+
.unwrap()
1065+
),
1066+
format!("{:?}", Error::ResLimitFormat(format.to_string()))
1067+
);
1068+
}
1069+
1070+
// Check invalid resource arguments
1071+
let invalid_resources = ["foo", "", " "];
1072+
for res in invalid_resources.iter() {
1073+
let arg = format!("{}=2", res);
1074+
assert_eq!(
1075+
format!(
1076+
"{:?}",
1077+
Env::parse_resource_limits(&mut resource_limits, &*vec![arg])
1078+
.err()
1079+
.unwrap()
1080+
),
1081+
format!("{:?}", Error::ResLimitArgument(res.to_string()))
1082+
);
1083+
}
1084+
1085+
// Check invalid limit values
1086+
let invalid_values = ["foo", "2.3", "2-3", " "];
1087+
for val in invalid_values.iter() {
1088+
let arg = format!("fsize={}", val);
1089+
assert_eq!(
1090+
format!(
1091+
"{:?}",
1092+
Env::parse_resource_limits(&mut resource_limits, &*vec![arg])
1093+
.err()
1094+
.unwrap()
1095+
),
1096+
format!(
1097+
"{:?}",
1098+
Error::ResLimitValue(
1099+
val.to_string(),
1100+
"invalid digit found in string".to_string()
1101+
)
1102+
)
1103+
);
1104+
}
1105+
1106+
// Check valid cases
1107+
let resources = [FSIZE_ARG, NO_FILE_ARG];
1108+
for resource in resources.iter() {
1109+
let arg = vec![resource.to_string() + &"=4098".to_string()];
1110+
Env::parse_resource_limits(&mut resource_limits, &*arg).unwrap();
1111+
}
1112+
}
1113+
10091114
#[test]
10101115
#[cfg(target_arch = "aarch64")]
10111116
fn test_copy_cache_info() {

src/jailer/src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub enum Error {
5757
ReadLine(PathBuf, io::Error),
5858
ReadToString(PathBuf, io::Error),
5959
RegEx(regex::Error),
60+
ResLimitArgument(String),
61+
ResLimitFormat(String),
62+
ResLimitValue(String, String),
6063
RmOldRootDir(io::Error),
6164
SetCurrentDir(io::Error),
6265
SetNetNs(io::Error),
@@ -191,6 +194,11 @@ impl fmt::Display for Error {
191194
format!("Failed to read file {:?} into a string: {}", path, err).replace("\"", "")
192195
),
193196
RegEx(ref err) => write!(f, "Regex failed: {:?}", err),
197+
ResLimitArgument(ref arg) => write!(f, "Invalid resource argument: {}", arg,),
198+
ResLimitFormat(ref arg) => write!(f, "Invalid format for resources limits: {}", arg,),
199+
ResLimitValue(ref arg, ref err) => {
200+
write!(f, "Invalid limit value for resource: {}: {}", arg, err)
201+
}
194202
RmOldRootDir(ref err) => write!(f, "Failed to remove old jail root directory: {}", err),
195203
SetCurrentDir(ref err) => write!(f, "Failed to change current directory: {}", err),
196204
SetNetNs(ref err) => write!(f, "Failed to join network namespace: netns: {}", err),
@@ -282,6 +290,15 @@ pub fn build_arg_parser() -> ArgParser<'static> {
282290
<cgroup_file>=<value> (e.g cpu.shares=10). This argument can be used \
283291
multiple times to add multiple cgroups.",
284292
))
293+
.arg(Argument::new("resource-limit").allow_multiple(true).help(
294+
"Resource limit values to be set by the jailer. It must follow this format: \
295+
<resource>=<value> (e.g no-file=1024). This argument can be used \
296+
multiple times to add multiple resource limits. \
297+
Current available resource values are:\n\
298+
\t\tfsize: The maximum size in bytes for files created by the process.\n\
299+
\t\tno-file: Specifies a value one greater than the maximum file descriptor number \
300+
that can be opened by this process.",
301+
))
285302
.arg(
286303
Argument::new("version")
287304
.takes_value(false)
@@ -645,6 +662,21 @@ mod tests {
645662
format!("{}", Error::RegEx(err_regex.clone())),
646663
format!("Regex failed: {:?}", err_regex),
647664
);
665+
assert_eq!(
666+
format!("{}", Error::ResLimitArgument("foo".to_string())),
667+
"Invalid resource argument: foo",
668+
);
669+
assert_eq!(
670+
format!("{}", Error::ResLimitFormat("foo".to_string())),
671+
"Invalid format for resources limits: foo",
672+
);
673+
assert_eq!(
674+
format!(
675+
"{}",
676+
Error::ResLimitValue("foo".to_string(), "bar".to_string())
677+
),
678+
"Invalid limit value for resource: foo: bar",
679+
);
648680
assert_eq!(
649681
format!("{}", Error::RmOldRootDir(io::Error::from_raw_os_error(42))),
650682
"Failed to remove old jail root directory: No message of desired type (os error 42)",

src/jailer/src/resource_limits.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
#![allow(unused)]
54
use super::{Error, Result};
65
use std::fmt;
76
use std::fmt::{Display, Formatter};
87
use utils::syscall::SyscallReturnCode;
98

109
// Default limit for the maximum number of file descriptors open at a time.
1110
const NO_FILE: u64 = 2048;
11+
// File size resource argument name.
12+
pub(crate) const FSIZE_ARG: &str = "fsize";
13+
// Number of files resource argument name.
14+
pub(crate) const NO_FILE_ARG: &str = "no-file";
1215

1316
#[derive(Clone, Copy)]
1417
pub enum Resource {

0 commit comments

Comments
 (0)