Skip to content

Commit 6065405

Browse files
ita93roypat
authored andcommitted
Fixing kernel commandline parameter validation
According to linux kernel document, the value of command line parameter can contains spaces (with double quotes) or equals. This change will also reject the parameter value that contains quotes in the middle. Signed-off-by: Nguyen Dinh Phi <[email protected]>
1 parent c879ef6 commit 6065405

File tree

2 files changed

+65
-9
lines changed

2 files changed

+65
-9
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# Upcoming Release
2+
## Changed
3+
- [[#148](https://github.com/rust-vmm/linux-loader/pull/148)] Fixing kernel commandline parameter validation
4+
This change allows parameter values containing spaces in the middle, provided they are enclosed in quotes. However,
5+
strings with quotes in the middle will no longer be accepted as valid parameter values.
26

37
# [v0.13.0]
48

src/cmdline/mod.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ pub enum Error {
3737
MmioSize,
3838
/// Operation would have made the command line too large.
3939
TooLarge,
40+
/// Double-quotes can be used to protect spaces in values
41+
NoQuoteSpace,
42+
/// A double quote that is in the middle of the value
43+
InvalidQuote,
4044
}
4145

4246
impl fmt::Display for Error {
@@ -56,6 +60,11 @@ impl fmt::Display for Error {
5660
"0-sized virtio MMIO device passed to the kernel command line builder."
5761
),
5862
Error::TooLarge => write!(f, "Inserting string would make command line too long."),
63+
Error::NoQuoteSpace => write!(
64+
f,
65+
"Value that contains spaces need to be surrounded by quotes"
66+
),
67+
Error::InvalidQuote => write!(f, "Double quote can not be in the middle of the value"),
5968
}
6069
}
6170
}
@@ -79,7 +88,26 @@ fn valid_str(s: &str) -> Result<()> {
7988
}
8089
}
8190

82-
fn valid_element(s: &str) -> Result<()> {
91+
fn is_quoted(s: &str) -> bool {
92+
if s.len() < 2 {
93+
return false;
94+
}
95+
if let Some(first_char) = s.chars().next() {
96+
if let Some(last_char) = s.chars().last() {
97+
return first_char == '"' && last_char == '"';
98+
}
99+
}
100+
false
101+
}
102+
103+
fn contains_double_quotes(s: &str) -> bool {
104+
if s.len() < 3 {
105+
return false;
106+
}
107+
return s.chars().skip(1).take(s.len() - 2).any(|c| c == '"');
108+
}
109+
110+
fn valid_key(s: &str) -> Result<()> {
83111
if !s.chars().all(valid_char) {
84112
Err(Error::InvalidAscii)
85113
} else if s.contains(' ') {
@@ -91,6 +119,18 @@ fn valid_element(s: &str) -> Result<()> {
91119
}
92120
}
93121

122+
fn valid_value(s: &str) -> Result<()> {
123+
if !s.chars().all(valid_char) {
124+
Err(Error::InvalidAscii)
125+
} else if contains_double_quotes(s) {
126+
Err(Error::InvalidQuote)
127+
} else if s.contains(' ') && !is_quoted(s) {
128+
Err(Error::NoQuoteSpace)
129+
} else {
130+
Ok(())
131+
}
132+
}
133+
94134
/// A builder for a kernel command line string that validates the string as it's being built.
95135
///
96136
/// # Examples
@@ -155,8 +195,8 @@ impl Cmdline {
155195
let k = key.as_ref();
156196
let v = val.as_ref();
157197

158-
valid_element(k)?;
159-
valid_element(v)?;
198+
valid_key(k)?;
199+
valid_value(v)?;
160200

161201
let kv_str = format!("{}={}", k, v);
162202

@@ -186,7 +226,7 @@ impl Cmdline {
186226
pub fn insert_multiple<T: AsRef<str>>(&mut self, key: T, vals: &[T]) -> Result<()> {
187227
let k = key.as_ref();
188228

189-
valid_element(k)?;
229+
valid_key(k)?;
190230
if vals.is_empty() {
191231
return Err(Error::MissingVal(k.to_string()));
192232
}
@@ -196,7 +236,7 @@ impl Cmdline {
196236
k,
197237
vals.iter()
198238
.map(|v| -> Result<&str> {
199-
valid_element(v.as_ref())?;
239+
valid_value(v.as_ref())?;
200240
Ok(v.as_ref())
201241
})
202242
.collect::<Result<Vec<&str>>>()?
@@ -567,20 +607,29 @@ mod tests {
567607
fn test_insert_space() {
568608
let mut cl = Cmdline::new(100).unwrap();
569609
assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
570-
assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
610+
assert_eq!(cl.insert("a", "b "), Err(Error::NoQuoteSpace));
571611
assert_eq!(cl.insert("a ", "b "), Err(Error::HasSpace));
572612
assert_eq!(cl.insert(" a", "b"), Err(Error::HasSpace));
613+
assert_eq!(cl.insert("a", "hello \"world"), Err(Error::InvalidQuote));
614+
assert_eq!(
615+
cl.insert("a", "\"foor bar\" \"foor bar\""),
616+
Err(Error::InvalidQuote)
617+
);
573618
assert_eq!(cl.as_cstring().unwrap().as_bytes_with_nul(), b"\0");
619+
assert!(cl.insert("a", "\"b b\"").is_ok());
620+
assert!(cl.insert("c", "\" d\"").is_ok());
621+
assert_eq!(
622+
cl.as_cstring().unwrap().as_bytes_with_nul(),
623+
b"a=\"b b\" c=\" d\"\0"
624+
);
574625
}
575626

576627
#[test]
577628
fn test_insert_equals() {
578629
let mut cl = Cmdline::new(100).unwrap();
579630
assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
580-
assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
581631
assert_eq!(cl.insert("a=", "b "), Err(Error::HasEquals));
582632
assert_eq!(cl.insert("=a", "b"), Err(Error::HasEquals));
583-
assert_eq!(cl.insert("a", "=b"), Err(Error::HasEquals));
584633
assert_eq!(cl.as_cstring().unwrap().as_bytes_with_nul(), b"\0");
585634
}
586635

@@ -702,7 +751,10 @@ mod tests {
702751
cl.insert_multiple("foo", &no_vals),
703752
Err(Error::MissingVal("foo".to_string()))
704753
);
705-
assert_eq!(cl.insert_multiple("foo", &["bar "]), Err(Error::HasSpace));
754+
assert_eq!(
755+
cl.insert_multiple("foo", &["bar "]),
756+
Err(Error::NoQuoteSpace)
757+
);
706758
assert_eq!(
707759
cl.insert_multiple("foo", &["bar", "baz"]),
708760
Err(Error::TooLarge)

0 commit comments

Comments
 (0)