Skip to content

Commit 65d1382

Browse files
committed
add "secret_free" parameter to /machine-config endpoint
This will later indicate to Firecracker that guest memory should be backed by guest_memfd. Signed-off-by: Patrick Roy <roypat@amazon.co.uk>
1 parent fd3ab21 commit 65d1382

File tree

6 files changed

+119
-2
lines changed

6 files changed

+119
-2
lines changed

src/firecracker/swagger/firecracker.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,11 @@ definitions:
10761076
description:
10771077
Aarch64 only. If non-zero, causes Firecracker to allocate a specific memory area of this size (in MiB)
10781078
which the guest will use as swiotlb region for all I/O.
1079+
secret_free:
1080+
type: boolean
1081+
description:
1082+
If enabled, guest memory will be unmapped from the host kernel's address space, providing additional
1083+
protection against transitive execution issues. All I/O must then go through a swiotlb region.
10791084

10801085
MemoryBackend:
10811086
type: object

src/vmm/src/resources.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,14 @@ pub struct VmResources {
110110
}
111111

112112
impl VmResources {
113+
/// Whether this [`VmResources`] object contains any devices that require host kernel access
114+
/// into guest memory.
115+
pub fn has_any_io_devices(&self) -> bool {
116+
!self.block.devices.is_empty()
117+
|| self.vsock.get().is_some()
118+
|| self.net_builder.iter().next().is_some()
119+
}
120+
113121
/// Configures Vmm resources as described by the `config_json` param.
114122
pub fn from_json(
115123
config_json: &str,
@@ -217,6 +225,11 @@ impl VmResources {
217225
BalloonConfigError::IncompatibleWith("huge pages"),
218226
));
219227
}
228+
if self.machine_config.mem_config.secret_free {
229+
return Err(ResourcesError::BalloonDevice(
230+
BalloonConfigError::IncompatibleWith("secret freedom"),
231+
));
232+
}
220233
}
221234

222235
SharedDeviceType::Vsock(vsock) => {
@@ -256,12 +269,28 @@ impl VmResources {
256269
return Err(MachineConfigError::IncompatibleBalloonSize);
257270
}
258271

272+
if self.has_any_io_devices()
273+
&& self.machine_config.mem_config.secret_free
274+
&& !self.swiotlb_used()
275+
{
276+
return Err(MachineConfigError::Incompatible(
277+
"secret freedom",
278+
"I/O without swiotlb",
279+
));
280+
}
281+
259282
if self.balloon.get().is_some() && updated.huge_pages != HugePageConfig::None {
260283
return Err(MachineConfigError::Incompatible(
261284
"balloon device",
262285
"huge pages",
263286
));
264287
}
288+
if self.balloon.get().is_some() && updated.mem_config.secret_free {
289+
return Err(MachineConfigError::Incompatible(
290+
"balloon device",
291+
"secret freedom",
292+
));
293+
}
265294
self.machine_config = updated;
266295

267296
Ok(())
@@ -320,6 +349,10 @@ impl VmResources {
320349
return Err(BalloonConfigError::IncompatibleWith("huge pages"));
321350
}
322351

352+
if self.machine_config.mem_config.secret_free {
353+
return Err(BalloonConfigError::IncompatibleWith("secret freedom"));
354+
}
355+
323356
self.balloon.set(config)
324357
}
325358

@@ -343,6 +376,13 @@ impl VmResources {
343376
&mut self,
344377
block_device_config: BlockDeviceConfig,
345378
) -> Result<(), DriveError> {
379+
if self.has_any_io_devices()
380+
&& self.machine_config.mem_config.secret_free
381+
&& !self.swiotlb_used()
382+
{
383+
return Err(DriveError::SecretFreeWithoutSwiotlb);
384+
}
385+
346386
self.block.insert(block_device_config)
347387
}
348388

@@ -351,12 +391,26 @@ impl VmResources {
351391
&mut self,
352392
body: NetworkInterfaceConfig,
353393
) -> Result<(), NetworkInterfaceError> {
394+
if self.has_any_io_devices()
395+
&& self.machine_config.mem_config.secret_free
396+
&& !self.swiotlb_used()
397+
{
398+
return Err(NetworkInterfaceError::SecretFreeWithoutSwiotlb);
399+
}
400+
354401
let _ = self.net_builder.build(body)?;
355402
Ok(())
356403
}
357404

358405
/// Sets a vsock device to be attached when the VM starts.
359406
pub fn set_vsock_device(&mut self, config: VsockDeviceConfig) -> Result<(), VsockConfigError> {
407+
if self.has_any_io_devices()
408+
&& self.machine_config.mem_config.secret_free
409+
&& !self.swiotlb_used()
410+
{
411+
return Err(VsockConfigError::SecretFreeWithoutSwiotlb);
412+
}
413+
360414
self.vsock.insert(config)
361415
}
362416

src/vmm/src/vmm_config/drive.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub enum DriveError {
2424
DeviceUpdate(VmmError),
2525
/// A root block device already exists!
2626
RootBlockDeviceAlreadyAdded,
27+
/// A block device was added to a secret free VM that doesnt have a swiotlb region.
28+
SecretFreeWithoutSwiotlb,
2729
}
2830

2931
/// Use this structure to set up the Block Device before booting the kernel.

src/vmm/src/vmm_config/machine_config.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ pub struct MemoryConfig {
9999
#[cfg(target_arch = "aarch64")]
100100
#[serde(default)]
101101
pub initial_swiotlb_size: usize,
102+
/// Whether guest_memfd should be used to back normal guest memory. If this is enabled
103+
/// and any devices are attached to the VM, then initial_swiotlb_size must be non-zero,
104+
/// as I/O into secret free memory is not possible.
105+
#[serde(default)]
106+
pub secret_free: bool,
102107
}
103108

104109
/// Struct used in PUT `/machine-config` API call.
@@ -289,6 +294,7 @@ impl MachineConfig {
289294
let mem_size_mib = update.mem_size_mib.unwrap_or(self.mem_size_mib);
290295
let page_config = update.huge_pages.unwrap_or(self.huge_pages);
291296
let mem_config = update.mem_config.unwrap_or(self.mem_config);
297+
let track_dirty_pages = update.track_dirty_pages.unwrap_or(self.track_dirty_pages);
292298

293299
if mem_size_mib == 0 || !page_config.is_valid_mem_size(mem_size_mib) {
294300
return Err(MachineConfigError::InvalidMemorySize);
@@ -301,6 +307,20 @@ impl MachineConfig {
301307
return Err(MachineConfigError::InvalidSwiotlbRegionSize);
302308
}
303309

310+
if mem_config.secret_free && page_config != HugePageConfig::None {
311+
return Err(MachineConfigError::Incompatible(
312+
"secret freedom",
313+
"huge pages",
314+
));
315+
}
316+
317+
if mem_config.secret_free && track_dirty_pages {
318+
return Err(MachineConfigError::Incompatible(
319+
"secret freedom",
320+
"diff snapshots",
321+
));
322+
}
323+
304324
let cpu_template = match update.cpu_template {
305325
None => self.cpu_template.clone(),
306326
Some(StaticCpuTemplate::None) => None,
@@ -313,7 +333,7 @@ impl MachineConfig {
313333
mem_config,
314334
smt,
315335
cpu_template,
316-
track_dirty_pages: update.track_dirty_pages.unwrap_or(self.track_dirty_pages),
336+
track_dirty_pages,
317337
huge_pages: page_config,
318338
#[cfg(feature = "gdb")]
319339
gdb_socket_path: update.gdb_socket_path.clone(),
@@ -325,7 +345,7 @@ impl MachineConfig {
325345
mod tests {
326346
use crate::cpu_config::templates::{CpuTemplateType, CustomCpuTemplate, StaticCpuTemplate};
327347
use crate::vmm_config::machine_config::{
328-
HugePageConfig, MachineConfig, MachineConfigError, MachineConfigUpdate,
348+
HugePageConfig, MachineConfig, MachineConfigError, MachineConfigUpdate, MemoryConfig,
329349
};
330350

331351
#[test]
@@ -379,6 +399,38 @@ mod tests {
379399
.unwrap();
380400
assert_eq!(updated.huge_pages, HugePageConfig::Hugetlbfs2M);
381401
assert_eq!(updated.mem_size_mib, 32);
402+
403+
let res = mconf.update(&MachineConfigUpdate {
404+
huge_pages: Some(HugePageConfig::Hugetlbfs2M),
405+
mem_config: Some(MemoryConfig {
406+
secret_free: true,
407+
..Default::default()
408+
}),
409+
..Default::default()
410+
});
411+
assert_eq!(
412+
res,
413+
Err(MachineConfigError::Incompatible(
414+
"secret freedom",
415+
"huge pages"
416+
))
417+
);
418+
419+
let res = mconf.update(&MachineConfigUpdate {
420+
track_dirty_pages: Some(true),
421+
mem_config: Some(MemoryConfig {
422+
secret_free: true,
423+
..Default::default()
424+
}),
425+
..Default::default()
426+
});
427+
assert_eq!(
428+
res,
429+
Err(MachineConfigError::Incompatible(
430+
"secret freedom",
431+
"diff snapshots"
432+
))
433+
);
382434
}
383435

384436
#[test]

src/vmm/src/vmm_config/net.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ pub enum NetworkInterfaceError {
7171
GuestMacAddressInUse(String),
7272
/// Cannot open/create the tap device: {0}
7373
OpenTap(#[from] TapError),
74+
/// A net device was added to a secret free VM that doesnt have a swiotlb region.
75+
SecretFreeWithoutSwiotlb,
7476
}
7577

7678
/// Builder for a list of network devices.

src/vmm/src/vmm_config/vsock.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub enum VsockConfigError {
1717
CreateVsockBackend(VsockUnixBackendError),
1818
/// Cannot create vsock device: {0}
1919
CreateVsockDevice(VsockError),
20+
/// A vsock device was added to a secret free VM that doesnt have a swiotlb region.
21+
SecretFreeWithoutSwiotlb,
2022
}
2123

2224
/// This struct represents the strongly typed equivalent of the json body

0 commit comments

Comments
 (0)