Skip to content

Commit e74864b

Browse files
danielverkampcrosvm LUCI
authored and
crosvm LUCI
committed
kernel_cmdline: remove capacity from Cmdline
Rather than checking and re-checking the capacity on every insert, just check the final length of the string when the command line is finished. BUG=b:362168475 Change-Id: I0cae6a76f64aaeffbfd0b71dd18c2e0dc96f0a11 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/5838025 Commit-Queue: Daniel Verkamp <[email protected]> Reviewed-by: Junichi Uekawa <[email protected]> Reviewed-by: Keiichi Watanabe <[email protected]>
1 parent f727b45 commit e74864b

File tree

5 files changed

+75
-73
lines changed

5 files changed

+75
-73
lines changed

aarch64/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,9 @@ impl arch::LinuxArch for AArch64 {
815815
components.cpu_capacity,
816816
components.cpu_frequencies,
817817
fdt_address,
818-
cmdline.as_str(),
818+
cmdline
819+
.as_str_with_max_len(AARCH64_CMDLINE_MAX_SIZE - 1)
820+
.map_err(Error::Cmdline)?,
819821
(payload.entry(), payload.size() as usize),
820822
initrd,
821823
components.android_fstab,
@@ -1160,7 +1162,7 @@ impl<T: VcpuAArch64> arch::GdbOps<T> for AArch64 {
11601162
impl AArch64 {
11611163
/// This returns a base part of the kernel command for this architecture
11621164
fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
1163-
let mut cmdline = kernel_cmdline::Cmdline::new(AARCH64_CMDLINE_MAX_SIZE);
1165+
let mut cmdline = kernel_cmdline::Cmdline::new();
11641166
cmdline.insert_str("panic=-1").unwrap();
11651167
cmdline
11661168
}

arch/src/serial.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ mod tests {
252252

253253
#[test]
254254
fn get_serial_cmdline_default() {
255-
let mut cmdline = Cmdline::new(4096);
255+
let mut cmdline = Cmdline::new();
256256
let mut serial_parameters = BTreeMap::new();
257257
let io_bus = Bus::new(BusType::Io);
258258
let evt1_3 = Event::new().unwrap();
@@ -279,7 +279,7 @@ mod tests {
279279

280280
#[test]
281281
fn get_serial_cmdline_virtio_console() {
282-
let mut cmdline = Cmdline::new(4096);
282+
let mut cmdline = Cmdline::new();
283283
let mut serial_parameters = BTreeMap::new();
284284
let io_bus = Bus::new(BusType::Io);
285285
let evt1_3 = Event::new().unwrap();
@@ -325,7 +325,7 @@ mod tests {
325325

326326
#[test]
327327
fn get_serial_cmdline_virtio_console_serial_earlycon() {
328-
let mut cmdline = Cmdline::new(4096);
328+
let mut cmdline = Cmdline::new();
329329
let mut serial_parameters = BTreeMap::new();
330330
let io_bus = Bus::new(BusType::Io);
331331
let evt1_3 = Event::new().unwrap();
@@ -391,7 +391,7 @@ mod tests {
391391

392392
#[test]
393393
fn get_serial_cmdline_virtio_console_invalid_earlycon() {
394-
let mut cmdline = Cmdline::new(4096);
394+
let mut cmdline = Cmdline::new();
395395
let mut serial_parameters = BTreeMap::new();
396396
let io_bus = Bus::new(BusType::Io);
397397
let evt1_3 = Event::new().unwrap();

kernel_cmdline/src/kernel_cmdline.rs

+57-61
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ pub enum Error {
2323
#[error("string contains non-printable ASCII character")]
2424
InvalidAscii,
2525
/// Operation would have made the command line too large.
26-
#[error("inserting string would make command line too long")]
27-
TooLarge,
26+
#[error("command line length {0} exceeds maximum {1}")]
27+
TooLarge(usize, usize),
2828
}
2929

3030
/// Specialized Result type for command line operations.
@@ -54,59 +54,36 @@ fn valid_element(s: &str) -> Result<()> {
5454
}
5555
}
5656

57-
/// A builder for a kernel command line string that validates the string as its being built. A
58-
/// `CString` can be constructed from this directly using `CString::new`.
57+
/// A builder for a kernel command line string that validates the string as it is built.
58+
#[derive(Default)]
5959
pub struct Cmdline {
6060
line: String,
61-
capacity: usize,
6261
}
6362

6463
impl Cmdline {
65-
/// Constructs an empty Cmdline with the given capacity, which includes the nul terminator.
66-
/// Capacity must be greater than 0.
67-
pub fn new(capacity: usize) -> Cmdline {
68-
assert_ne!(capacity, 0);
69-
Cmdline {
70-
line: String::new(),
71-
capacity,
72-
}
73-
}
74-
75-
fn has_capacity(&self, more: usize) -> Result<()> {
76-
let needs_space = if self.line.is_empty() { 0 } else { 1 };
77-
if self.line.len() + more + needs_space < self.capacity {
78-
Ok(())
79-
} else {
80-
Err(Error::TooLarge)
81-
}
64+
/// Constructs an empty Cmdline.
65+
pub fn new() -> Cmdline {
66+
Cmdline::default()
8267
}
8368

84-
fn start_push(&mut self) {
69+
fn push_space_if_needed(&mut self) {
8570
if !self.line.is_empty() {
8671
self.line.push(' ');
8772
}
8873
}
8974

90-
fn end_push(&mut self) {
91-
// This assert is always true because of the `has_capacity` check that each insert method
92-
// uses.
93-
assert!(self.line.len() < self.capacity);
94-
}
95-
9675
/// Validates and inserts a key value pair into this command line
9776
pub fn insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()> {
9877
let k = key.as_ref();
9978
let v = val.as_ref();
10079

10180
valid_element(k)?;
10281
valid_element(v)?;
103-
self.has_capacity(k.len() + v.len() + 1)?;
10482

105-
self.start_push();
83+
self.push_space_if_needed();
10684
self.line.push_str(k);
10785
self.line.push('=');
10886
self.line.push_str(v);
109-
self.end_push();
11087

11188
Ok(())
11289
}
@@ -116,11 +93,8 @@ impl Cmdline {
11693
let s = slug.as_ref();
11794
valid_str(s)?;
11895

119-
self.has_capacity(s.len())?;
120-
121-
self.start_push();
96+
self.push_space_if_needed();
12297
self.line.push_str(s);
123-
self.end_push();
12498

12599
Ok(())
126100
}
@@ -129,42 +103,64 @@ impl Cmdline {
129103
pub fn as_str(&self) -> &str {
130104
self.line.as_str()
131105
}
132-
}
133106

134-
impl From<Cmdline> for Vec<u8> {
135-
fn from(c: Cmdline) -> Vec<u8> {
136-
c.line.into_bytes()
107+
/// Returns the current command line as a string with a maximum length.
108+
///
109+
/// # Arguments
110+
///
111+
/// `max_len`: maximum number of bytes (not including NUL terminator)
112+
pub fn as_str_with_max_len(&self, max_len: usize) -> Result<&str> {
113+
let s = self.line.as_str();
114+
if s.len() <= max_len {
115+
Ok(s)
116+
} else {
117+
Err(Error::TooLarge(s.len(), max_len))
118+
}
119+
}
120+
121+
/// Converts the command line into a `Vec<u8>` with a maximum length.
122+
///
123+
/// # Arguments
124+
///
125+
/// `max_len`: maximum number of bytes (not including NUL terminator)
126+
pub fn into_bytes_with_max_len(self, max_len: usize) -> Result<Vec<u8>> {
127+
let bytes: Vec<u8> = self.line.into_bytes();
128+
if bytes.len() <= max_len {
129+
Ok(bytes)
130+
} else {
131+
Err(Error::TooLarge(bytes.len(), max_len))
132+
}
137133
}
138134
}
139135

140136
#[cfg(test)]
141137
mod tests {
142-
use std::ffi::CString;
143-
144138
use super::*;
145139

146140
#[test]
147141
fn insert_hello_world() {
148-
let mut cl = Cmdline::new(100);
142+
let mut cl = Cmdline::new();
149143
assert_eq!(cl.as_str(), "");
150144
assert!(cl.insert("hello", "world").is_ok());
151145
assert_eq!(cl.as_str(), "hello=world");
152146

153-
let s = CString::new(cl).expect("failed to create CString from Cmdline");
154-
assert_eq!(s, CString::new("hello=world").unwrap());
147+
let bytes = cl
148+
.into_bytes_with_max_len(100)
149+
.expect("failed to convert Cmdline into bytes");
150+
assert_eq!(bytes, b"hello=world");
155151
}
156152

157153
#[test]
158154
fn insert_multi() {
159-
let mut cl = Cmdline::new(100);
155+
let mut cl = Cmdline::new();
160156
assert!(cl.insert("hello", "world").is_ok());
161157
assert!(cl.insert("foo", "bar").is_ok());
162158
assert_eq!(cl.as_str(), "hello=world foo=bar");
163159
}
164160

165161
#[test]
166162
fn insert_space() {
167-
let mut cl = Cmdline::new(100);
163+
let mut cl = Cmdline::new();
168164
assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
169165
assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
170166
assert_eq!(cl.insert("a ", "b "), Err(Error::HasSpace));
@@ -174,7 +170,7 @@ mod tests {
174170

175171
#[test]
176172
fn insert_equals() {
177-
let mut cl = Cmdline::new(100);
173+
let mut cl = Cmdline::new();
178174
assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
179175
assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
180176
assert_eq!(cl.insert("a=", "b "), Err(Error::HasEquals));
@@ -185,15 +181,15 @@ mod tests {
185181

186182
#[test]
187183
fn insert_emoji() {
188-
let mut cl = Cmdline::new(100);
184+
let mut cl = Cmdline::new();
189185
assert_eq!(cl.insert("heart", "💖"), Err(Error::InvalidAscii));
190186
assert_eq!(cl.insert("💖", "love"), Err(Error::InvalidAscii));
191187
assert_eq!(cl.as_str(), "");
192188
}
193189

194190
#[test]
195191
fn insert_string() {
196-
let mut cl = Cmdline::new(13);
192+
let mut cl = Cmdline::new();
197193
assert_eq!(cl.as_str(), "");
198194
assert!(cl.insert_str("noapic").is_ok());
199195
assert_eq!(cl.as_str(), "noapic");
@@ -202,25 +198,25 @@ mod tests {
202198
}
203199

204200
#[test]
205-
fn insert_too_large() {
206-
let mut cl = Cmdline::new(4);
207-
assert_eq!(cl.insert("hello", "world"), Err(Error::TooLarge));
208-
assert_eq!(cl.insert("a", "world"), Err(Error::TooLarge));
209-
assert_eq!(cl.insert("hello", "b"), Err(Error::TooLarge));
201+
fn as_str_too_large() {
202+
let mut cl = Cmdline::new();
210203
assert!(cl.insert("a", "b").is_ok()); // start off with 3.
211-
assert_eq!(cl.insert("a", "b"), Err(Error::TooLarge)); // adds 4. " a=b"
212-
assert_eq!(cl.insert_str("a"), Err(Error::TooLarge));
213204
assert_eq!(cl.as_str(), "a=b");
205+
assert_eq!(cl.as_str_with_max_len(2), Err(Error::TooLarge(3, 2)));
206+
assert_eq!(cl.as_str_with_max_len(3), Ok("a=b"));
214207

215-
let mut cl = Cmdline::new(10);
208+
let mut cl = Cmdline::new();
216209
assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
217-
assert_eq!(cl.insert("c", "da"), Err(Error::TooLarge)); // adds 5 (including space) length
218210
assert!(cl.insert("c", "d").is_ok()); // adds 4 (including space) length
211+
assert_eq!(cl.as_str(), "ab=ba c=d");
212+
assert_eq!(cl.as_str_with_max_len(8), Err(Error::TooLarge(9, 8)));
213+
assert_eq!(cl.as_str_with_max_len(9), Ok("ab=ba c=d"));
219214

220-
let mut cl = Cmdline::new(10);
215+
let mut cl = Cmdline::new();
221216
assert!(cl.insert("ab", "ba").is_ok()); // adds 5 length
222-
assert_eq!(cl.insert_str("1234"), Err(Error::TooLarge)); // adds 5 (including space) length
223217
assert!(cl.insert_str("123").is_ok()); // adds 4 (including space) length
224218
assert_eq!(cl.as_str(), "ab=ba 123");
219+
assert_eq!(cl.as_str_with_max_len(8), Err(Error::TooLarge(9, 8)));
220+
assert_eq!(cl.as_str_with_max_len(9), Ok("ab=ba 123"));
225221
}
226222
}

riscv64/src/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,9 @@ impl arch::LinuxArch for Riscv64 {
391391
fdt_offset,
392392
aia_num_ids,
393393
aia_num_sources,
394-
cmdline.as_str(),
394+
cmdline
395+
.as_str_with_max_len(RISCV64_CMDLINE_MAX_SIZE - 1)
396+
.map_err(Error::Cmdline)?,
395397
initrd,
396398
timebase_freq,
397399
device_tree_overlays,
@@ -548,7 +550,7 @@ fn get_high_mmio_base_size(mem_size: u64, guest_phys_addr_bits: u8) -> (u64, u64
548550
}
549551

550552
fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
551-
let mut cmdline = kernel_cmdline::Cmdline::new(RISCV64_CMDLINE_MAX_SIZE);
553+
let mut cmdline = kernel_cmdline::Cmdline::new();
552554
cmdline.insert_str("panic=-1").unwrap();
553555
cmdline
554556
}

x86_64/src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,9 @@ impl X8664arch {
13541354
.get_slice_at_addr(guest_addr, CMDLINE_MAX_SIZE as usize)
13551355
.map_err(|_| Error::CommandLineOverflow)?;
13561356

1357-
let mut cmdline_bytes: Vec<u8> = cmdline.into();
1357+
let mut cmdline_bytes: Vec<u8> = cmdline
1358+
.into_bytes_with_max_len(CMDLINE_MAX_SIZE as usize - 1)
1359+
.map_err(Error::Cmdline)?;
13581360
cmdline_bytes.push(0u8); // Add NUL terminator.
13591361

13601362
cmdline_guest_mem_slice
@@ -1498,7 +1500,7 @@ impl X8664arch {
14981500

14991501
/// This returns a minimal kernel command for this architecture
15001502
pub fn get_base_linux_cmdline() -> kernel_cmdline::Cmdline {
1501-
let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
1503+
let mut cmdline = kernel_cmdline::Cmdline::new();
15021504
cmdline.insert_str("panic=-1").unwrap();
15031505

15041506
cmdline
@@ -2290,7 +2292,7 @@ mod tests {
22902292
fn cmdline_overflow() {
22912293
const MEM_SIZE: u64 = 0x1000;
22922294
let gm = GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap();
2293-
let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
2295+
let mut cmdline = kernel_cmdline::Cmdline::new();
22942296
cmdline.insert_str("12345").unwrap();
22952297
let cmdline_address = GuestAddress(MEM_SIZE - 5);
22962298
let err = X8664arch::load_cmdline(&gm, cmdline_address, cmdline).unwrap_err();
@@ -2301,7 +2303,7 @@ mod tests {
23012303
fn cmdline_write_end() {
23022304
const MEM_SIZE: u64 = 0x1000;
23032305
let gm = GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap();
2304-
let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE as usize);
2306+
let mut cmdline = kernel_cmdline::Cmdline::new();
23052307
cmdline.insert_str("1234").unwrap();
23062308
let mut cmdline_address = GuestAddress(45);
23072309
X8664arch::load_cmdline(&gm, cmdline_address, cmdline).unwrap();

0 commit comments

Comments
 (0)