Skip to content

Commit f3021c4

Browse files
committed
fix: Make PATCH /machine-config requests transactional
Ensure that modifications to the machine configuration are "all or nothing", e.g. no modifications happen if any part of the update causes an error (such as incorrect guest memory size). This fixes various possible bugs where, say, the validation of an updated vcpu field succeeds (meaning the vcpu field of the machine configuration is updated), but then a subsequent validation of the mem_size_mib field fails (say, because its 0), causing a error response to be returned to the user, yet not reverting the update to the vcpu field (in maybe more severe situations, it is possible to bypass the check that the balloon device is not configured to have a balloon larger than the total guest memory size, as mem_size_mib is updated before this check is being done). We fix this by using a Haskell-style approach to updating the configuration: In the update method, construct a new VmConfig object and return this; then only update the vm_config field on the VmResources struct if all validations have passed with the new vm_config object. Signed-off-by: Patrick Roy <[email protected]>
1 parent 59705ef commit f3021c4

File tree

3 files changed

+28
-23
lines changed

3 files changed

+28
-23
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
Now cpu-template-helper will print warnings if it encounters SVE registers
3737
during the conversion process. This is because cpu templates are limited
3838
to only modify registers less than 128 bits.
39+
- [#4414](https://github.com/firecracker-microvm/firecracker/pull/4360):
40+
Made `PATCH` requests to the `/machine-config` endpoint transactional, meaning
41+
Firecracker's configuration will be unchanged if the request returns an error.
42+
This fixes a bug where a microVM with incompatible balloon and guest memory size
43+
could be booted, due to the check for this condition happening after Firecracker's
44+
configuration was updated.
3945

4046
## [v1.6.0]
4147

src/vmm/src/resources.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,12 @@ impl VmResources {
242242

243243
/// Updates the configuration of the microVM.
244244
pub fn update_vm_config(&mut self, update: &MachineConfigUpdate) -> Result<(), VmConfigError> {
245-
self.vm_config.update(update)?;
245+
let updated = self.vm_config.update(update)?;
246246

247247
// The VM cannot have a memory size smaller than the target size
248248
// of the balloon device, if present.
249249
if self.balloon.get().is_some()
250-
&& self.vm_config.mem_size_mib
250+
&& updated.mem_size_mib
251251
< self
252252
.balloon
253253
.get_config()
@@ -257,6 +257,8 @@ impl VmResources {
257257
return Err(VmConfigError::IncompatibleBalloonSize);
258258
}
259259

260+
self.vm_config = updated;
261+
260262
Ok(())
261263
}
262264

src/vmm/src/vmm_config/machine_config.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,12 @@ impl VmConfig {
122122
self.cpu_template = Some(CpuTemplateType::Custom(cpu_template));
123123
}
124124

125-
/// Updates `VmConfig` with `MachineConfigUpdate`.
126-
/// Mapping for cpu tempalte update:
125+
/// Updates [`VmConfig`] with [`MachineConfigUpdate`].
126+
/// Mapping for cpu template update:
127127
/// StaticCpuTemplate::None -> None
128-
/// StaticCpuTemplate::Other -> Some(CustomCpuTemplate::Static(Other))
129-
pub fn update(&mut self, update: &MachineConfigUpdate) -> Result<(), VmConfigError> {
128+
/// StaticCpuTemplate::Other -> Some(CustomCpuTemplate::Static(Other)),
129+
/// Returns the updated `VmConfig` object.
130+
pub fn update(&self, update: &MachineConfigUpdate) -> Result<VmConfig, VmConfigError> {
130131
let vcpu_count = update.vcpu_count.unwrap_or(self.vcpu_count);
131132

132133
let smt = update.smt.unwrap_or(self.smt);
@@ -146,29 +147,25 @@ impl VmConfig {
146147
return Err(VmConfigError::InvalidVcpuCount);
147148
}
148149

149-
self.vcpu_count = vcpu_count;
150-
self.smt = smt;
151-
152150
let mem_size_mib = update.mem_size_mib.unwrap_or(self.mem_size_mib);
153151

154152
if mem_size_mib == 0 {
155153
return Err(VmConfigError::InvalidMemorySize);
156154
}
157155

158-
self.mem_size_mib = mem_size_mib;
159-
160-
if let Some(cpu_template) = update.cpu_template {
161-
self.cpu_template = match cpu_template {
162-
StaticCpuTemplate::None => None,
163-
other => Some(CpuTemplateType::Static(other)),
164-
};
165-
}
166-
167-
if let Some(track_dirty_pages) = update.track_dirty_pages {
168-
self.track_dirty_pages = track_dirty_pages;
169-
}
170-
171-
Ok(())
156+
let cpu_template = match update.cpu_template {
157+
None => self.cpu_template.clone(),
158+
Some(StaticCpuTemplate::None) => None,
159+
Some(other) => Some(CpuTemplateType::Static(other)),
160+
};
161+
162+
Ok(VmConfig {
163+
vcpu_count,
164+
mem_size_mib,
165+
smt,
166+
cpu_template,
167+
track_dirty_pages: update.track_dirty_pages.unwrap_or(self.track_dirty_pages),
168+
})
172169
}
173170
}
174171

0 commit comments

Comments
 (0)