Skip to content

physicalmem: don't claim low addresses#2453

Open
zyuiop wants to merge 1 commit into
hermit-os:mainfrom
zyuiop:fix/physical-mem-low-addresses
Open

physicalmem: don't claim low addresses#2453
zyuiop wants to merge 1 commit into
hermit-os:mainfrom
zyuiop:fix/physical-mem-low-addresses

Conversation

@zyuiop
Copy link
Copy Markdown
Contributor

@zyuiop zyuiop commented Jun 1, 2026

Context

While working on patches for AMD SEV-SNP, we use the DeviceMapper with an offset early at boot to allocate a (plaintext) page to communicate with the hypervisor (the GHCB).

We observed that randomly an application compiled with hermit would crash or enter a spin-loop sometimes around the introduction of the IDT. This bug was random in that recompilation with some changes at unrelated places in the code could eliminate the bug. This made debugging "fun" (as in: adding a print makes the bug disappear fun).

This clearly screams of "memory corruption" somewhere. But where, actually? I don't really know, but this patch is an attempt at fixing one possible source.

What this does, and why?

This fix is actually two fixes in one.

  • First, map_frame_range (in physicalmem.rs) will, when an offset is used, map all pages also at a fixed offset. This will obviously require spawning new page tables, and it turns out that the physical free list may at this point contain 0x0 as a valid frame, which may get picked up. I don't know if it's bad-bad, but since the page table is identity mapped, this means that accessing the page table entry requires dereferencing virtual address 0, aka the null pointer, which certainly cannot be good.

  • Second, it turns out that the SMP boot code has a fixed address of 0x8000. This address is only the 8th frame in the free list, so you can be sure it is going to be used, either by the offset mapping, or later on when mapping the heap. However, when the SMP boot code is loaded, it will overwrite that page table entry, and corrupt the page table. I guess this bug can happen if you use a heap intensive application, it will reach a point where it accesses a part of the heap which is corrupt and crash. Maybe. I'm not certain.

To avoid these two problems, we propose a quickfix: just ignore the first 2 MB of physical addresses.

What else?

The detect_from_fdt method looks a bit "dangerous" to me, in that it detects memory and immediately uses it to map it (sometimes). In most cases, no mapping is created, because UEFI already identity mapped everything, but in the cases where it does use it immediately, it may use pages that should be reserved and would have left the free list later in the procedure. The fact that pages are mapped immediately may cause issues. But not sure.

@mkroening mkroening self-assigned this Jun 1, 2026
@mkroening mkroening self-requested a review June 1, 2026 15:18
@zyuiop zyuiop force-pushed the fix/physical-mem-low-addresses branch 2 times, most recently from 2d3c070 to cfe9c81 Compare June 1, 2026 15:23
@zyuiop zyuiop force-pushed the fix/physical-mem-low-addresses branch from cfe9c81 to cd32dc9 Compare June 1, 2026 15:27
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Benchmark Results

Details
Benchmark Current: cd32dc9 Previous: a025f19 Performance Ratio
startup_benchmark Build Time 117.87 s 116.82 s 1.01
startup_benchmark File Size 0.73 MB 0.77 MB 0.95
Startup Time - 1 core 0.99 s (±0.02 s) 1.00 s (±0.03 s) 0.99
Startup Time - 2 cores 1.02 s (±0.04 s) 1.04 s (±0.05 s) 0.99
Startup Time - 4 cores 1.00 s (±0.03 s) 1.02 s (±0.05 s) 0.98
multithreaded_benchmark Build Time 115.36 s 117.48 s 0.98
multithreaded_benchmark File Size 0.87 MB 0.84 MB 1.04
Multithreaded Pi Efficiency - 2 Threads 92.91 % (±12.94 %) 85.06 % (±19.28 %) 1.09
Multithreaded Pi Efficiency - 4 Threads 45.17 % (±6.08 %) 42.20 % (±9.32 %) 1.07
Multithreaded Pi Efficiency - 8 Threads 24.92 % (±3.07 %) 23.57 % (±5.00 %) 1.06
micro_benchmarks Build Time 95.56 s 92.09 s 1.04
micro_benchmarks File Size 0.87 MB 0.84 MB 1.04
Scheduling time - 1 thread 72.40 ticks (±3.20 ticks) 73.19 ticks (±3.39 ticks) 0.99
Scheduling time - 2 threads 41.59 ticks (±4.17 ticks) 44.10 ticks (±6.54 ticks) 0.94
Micro - Time for syscall (getpid) 3.81 ticks (±0.21 ticks) 3.09 ticks (±0.16 ticks) 1.23
Memcpy speed - (built_in) block size 4096 75000.07 MByte/s (±51754.39 MByte/s) 73877.55 MByte/s (±50900.64 MByte/s) 1.02
Memcpy speed - (built_in) block size 1048576 29616.19 MByte/s (±24287.95 MByte/s) 29621.41 MByte/s (±24412.82 MByte/s) 1.00
Memcpy speed - (built_in) block size 16777216 23553.80 MByte/s (±19423.97 MByte/s) 26615.80 MByte/s (±21973.01 MByte/s) 0.88
Memset speed - (built_in) block size 4096 74964.56 MByte/s (±51730.73 MByte/s) 74082.46 MByte/s (±51039.25 MByte/s) 1.01
Memset speed - (built_in) block size 1048576 30370.51 MByte/s (±24732.01 MByte/s) 30381.08 MByte/s (±24836.90 MByte/s) 1.00
Memset speed - (built_in) block size 16777216 24294.07 MByte/s (±19934.39 MByte/s) 27365.04 MByte/s (±22427.19 MByte/s) 0.89
Memcpy speed - (rust) block size 4096 66634.09 MByte/s (±46559.25 MByte/s) 67998.13 MByte/s (±47775.53 MByte/s) 0.98
Memcpy speed - (rust) block size 1048576 29529.21 MByte/s (±24272.32 MByte/s) 29415.03 MByte/s (±24216.28 MByte/s) 1.00
Memcpy speed - (rust) block size 16777216 24321.97 MByte/s (±20047.53 MByte/s) 26645.15 MByte/s (±21980.78 MByte/s) 0.91
Memset speed - (rust) block size 4096 67055.71 MByte/s (±46853.50 MByte/s) 68410.01 MByte/s (±48051.67 MByte/s) 0.98
Memset speed - (rust) block size 1048576 30297.78 MByte/s (±24714.98 MByte/s) 30182.39 MByte/s (±24662.20 MByte/s) 1.00
Memset speed - (rust) block size 16777216 24920.48 MByte/s (±20396.08 MByte/s) 27320.66 MByte/s (±22364.88 MByte/s) 0.91
alloc_benchmarks Build Time 91.60 s 91.56 s 1.00
alloc_benchmarks File Size 0.81 MB 0.85 MB 0.96
Allocations - Allocation success 100.00 % 100.00 % 1
Allocations - Deallocation success 100.00 % 100.00 % 1
Allocations - Pre-fail Allocations 100.00 % 100.00 % 1
Allocations - Average Allocation time 4307.32 Ticks (±572.85 Ticks) 4553.50 Ticks (±57.46 Ticks) 0.95
Allocations - Average Allocation time (no fail) 4307.32 Ticks (±572.85 Ticks) 4553.50 Ticks (±57.46 Ticks) 0.95
Allocations - Average Deallocation time 676.00 Ticks (±102.89 Ticks) 1035.15 Ticks (±129.57 Ticks) 0.65
mutex_benchmark Build Time 93.45 s 88.37 s 1.06
mutex_benchmark File Size 0.88 MB 0.84 MB 1.04
Mutex Stress Test Average Time per Iteration - 1 Threads 14.00 ns (±0.69 ns) 14.02 ns (±0.84 ns) 1.00
Mutex Stress Test Average Time per Iteration - 2 Threads 16.20 ns (±10.70 ns) 17.80 ns (±4.95 ns) 0.91

This comment was automatically generated by workflow using github-action-benchmark.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants