Skip to content

fix(vmclock): validate guest address is plugged before activate write#5883

Merged
Manciukic merged 5 commits into
firecracker-microvm:mainfrom
Manciukic:fix-vmclock-sigsegv
Jun 10, 2026
Merged

fix(vmclock): validate guest address is plugged before activate write#5883
Manciukic merged 5 commits into
firecracker-microvm:mainfrom
Manciukic:fix-vmclock-sigsegv

Conversation

@Manciukic

@Manciukic Manciukic commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix SIGSEGV during snapshot restore when a malformed snapshot places the vmclock guest_address in an unplugged (PROT_NONE) hotpluggable memory region.

Crashing on such accesses is the right thing to do as they're unexpected, but we try to keep the snapshot restoration path clean to test it via the fuzzer.

Root Cause

  1. Malformed snapshot has plugged bitvec marking slots as unplugged
  2. restore_memory_regions calls mprotect(PROT_NONE) on unplugged slots
  3. VmClock::activate writes to guest_address (from snapshot) which falls in the unplugged region
  4. write_slicecopy_nonoverlapping → SIGSEGV on the protected page

Changes

  1. refactor(memory): Change slots_intersecting_range to return (GuestMemorySlot, bool) tuples, exposing the plugged state alongside each slot.
  2. feat(memory): Add check_range_plugged() to GuestRegionMmapExt and GuestMemoryExtension trait. Validates that a guest address range falls within plugged memory slots before performing writes.
  3. fix(vmclock): Call check_range_plugged() before write_slice in VmClock::activate, converting the SIGSEGV into a graceful error.

Testing

  • New unit test test_check_range_plugged covering plugged, unplugged, spanning, and zero-length cases
  • Existing test_slots_intersecting_range updated for new return type
  • Found by the snapshot fuzzer — 488 sig:11 crashes all reproduce this path

@Manciukic Manciukic force-pushed the fix-vmclock-sigsegv branch 2 times, most recently from da46f10 to 60d258b Compare May 13, 2026 11:47
@codecov

codecov Bot commented May 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.01%. Comparing base (9700741) to head (3771cb7).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5883      +/-   ##
==========================================
+ Coverage   83.00%   83.01%   +0.01%     
==========================================
  Files         277      277              
  Lines       30106    30129      +23     
==========================================
+ Hits        24989    25012      +23     
  Misses       5117     5117              
Flag Coverage Δ
5.10-m5n.metal 83.32% <100.00%> (+0.02%) ⬆️
5.10-m6a.metal 82.67% <100.00%> (+0.02%) ⬆️
5.10-m6g.metal 79.95% <100.00%> (+0.01%) ⬆️
5.10-m6i.metal 83.32% <100.00%> (+0.01%) ⬆️
5.10-m7a.metal-48xl 82.66% <100.00%> (+0.01%) ⬆️
5.10-m7g.metal 79.96% <100.00%> (+0.01%) ⬆️
5.10-m7i.metal-24xl 83.30% <100.00%> (+0.02%) ⬆️
5.10-m7i.metal-48xl 83.30% <100.00%> (+0.01%) ⬆️
5.10-m8g.metal-24xl 79.95% <100.00%> (+0.01%) ⬆️
5.10-m8g.metal-48xl 79.95% <100.00%> (+0.01%) ⬆️
5.10-m8i.metal-48xl 83.28% <100.00%> (+0.01%) ⬆️
5.10-m8i.metal-96xl 83.29% <100.00%> (+0.01%) ⬆️
6.1-m5n.metal 83.35% <100.00%> (+0.02%) ⬆️
6.1-m6a.metal 82.69% <100.00%> (+0.01%) ⬆️
6.1-m6g.metal 79.95% <100.00%> (+0.01%) ⬆️
6.1-m6i.metal 83.34% <100.00%> (+0.01%) ⬆️
6.1-m7a.metal-48xl 82.68% <100.00%> (+0.01%) ⬆️
6.1-m7g.metal 79.95% <100.00%> (+0.01%) ⬆️
6.1-m7i.metal-24xl 83.35% <100.00%> (+0.01%) ⬆️
6.1-m7i.metal-48xl 83.35% <100.00%> (+<0.01%) ⬆️
6.1-m8g.metal-24xl 79.95% <100.00%> (?)
6.1-m8g.metal-48xl 79.95% <100.00%> (+0.01%) ⬆️
6.1-m8i.metal-48xl 83.36% <100.00%> (+0.01%) ⬆️
6.1-m8i.metal-96xl 83.36% <100.00%> (+0.01%) ⬆️
6.18-m5n.metal 83.35% <100.00%> (+0.02%) ⬆️
6.18-m6a.metal 82.69% <100.00%> (+0.01%) ⬆️
6.18-m6g.metal 79.96% <100.00%> (?)
6.18-m6i.metal 83.34% <100.00%> (+0.01%) ⬆️
6.18-m7a.metal-48xl 82.68% <100.00%> (+0.01%) ⬆️
6.18-m7g.metal 79.95% <100.00%> (+<0.01%) ⬆️
6.18-m7i.metal-24xl 83.35% <100.00%> (+0.01%) ⬆️
6.18-m7i.metal-48xl 83.35% <100.00%> (+0.01%) ⬆️
6.18-m8g.metal-24xl 79.95% <100.00%> (+0.01%) ⬆️
6.18-m8g.metal-48xl 79.95% <100.00%> (+0.01%) ⬆️
6.18-m8i.metal-48xl 83.36% <100.00%> (+0.01%) ⬆️
6.18-m8i.metal-96xl 83.36% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Manciukic Manciukic marked this pull request as ready for review May 13, 2026 16:28
@Manciukic Manciukic added the Status: Awaiting review Indicates that a pull request is ready to be reviewed label May 13, 2026
@Manciukic Manciukic force-pushed the fix-vmclock-sigsegv branch from 53cddc3 to dc87382 Compare May 13, 2026 16:31

@ShadowCurse ShadowCurse left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like plugging a hole instead of fixing the issue (and all "fix the snapshot fuzzer" fixes fall into this category). It introduces unnecessary complexity (however small or big) without any real benefit.
Technically each write_slice/write_obj etc may do this type of check, yet it is absolutely valid to crash if somehow FC is tricked to writing to PROT_NONE memory.
We will need to rethink this snapshot fuzzing situation better sooner than later.

Comment thread src/vmm/src/vstate/memory.rs Outdated
@Manciukic Manciukic force-pushed the fix-vmclock-sigsegv branch 2 times, most recently from 1935bf2 to ad362f1 Compare May 22, 2026 09:04
@Manciukic

Copy link
Copy Markdown
Contributor Author

I agree it's just plugging a hole, but until we have resolved the underlying issue either with the fuzzer or with the checks on read/write in the region (which are tracked internally), we need a way to make the fuzzer green.

ShadowCurse
ShadowCurse previously approved these changes May 22, 2026
Comment thread src/vmm/src/devices/acpi/vmclock.rs
Manciukic added 4 commits June 9, 2026 13:41
Change slots_intersecting_range to return (GuestMemorySlot, bool)
tuples, where the bool indicates whether the slot is plugged. This
avoids needing a separate lock to check plugged state after finding
intersecting slots.

Signed-off-by: Riccardo Mancini <mancio@amazon.com>
Add check_range_plugged() to GuestRegionMmapExt and
GuestMemoryExtension trait. These methods verify that a guest address
range falls within plugged memory slots before performing writes,
using slots_intersecting_range to find the relevant slots.

Unplugged hotpluggable regions are mprotect'd to PROT_NONE, so writing
to them causes SIGSEGV. This provides a safe way to validate before
writing.

Signed-off-by: Riccardo Mancini <mancio@amazon.com>
Use check_range_plugged() before writing the vmclock struct to guest
memory during snapshot restore. A malformed snapshot can place the
vmclock guest_address in an unplugged (PROT_NONE) region, causing
SIGSEGV on the write_slice call.

Found by the snapshot fuzzer.

Signed-off-by: Riccardo Mancini <mancio@amazon.com>
Apply the same check_range_plugged guard to VmGenId::activate() that was
added to VmClock::activate(). A malformed snapshot can place the VMGenId
guest_address in an unplugged (PROT_NONE) region, causing a SIGSEGV on
the write_slice call during restore.

Signed-off-by: Riccardo Mancini <mancio@amazon.com>
@Manciukic Manciukic merged commit fa586c6 into firecracker-microvm:main Jun 10, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Status: Awaiting review Indicates that a pull request is ready to be reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants