Skip to content

Use PT_LOAD p_paddr directly when translating /proc/kcore segments#794

Open
demoray wants to merge 3 commits into
mainfrom
bcaswell/kcore-use-paddr
Open

Use PT_LOAD p_paddr directly when translating /proc/kcore segments#794
demoray wants to merge 3 commits into
mainfrom
bcaswell/kcore-use-paddr

Conversation

@demoray
Copy link
Copy Markdown
Collaborator

@demoray demoray commented May 18, 2026

kcore() previously computed each segment's physical address as
phdr.p_vaddr - (first_phdr.p_vaddr - first_iomem.start). That
assumes a single global virtual-to-physical offset across the entire
kernel address space — true on x86_64 (one direct map) but not on
PPC64 or any architecture where the kernel maps physical memory
through multiple non-contiguous virtual slabs. On those systems AVML
read from the wrong file offsets, producing corrupt acquisitions.

Use phdr.p_paddr directly. The kernel already populates it with the
physical address backing each PT_LOAD; no arithmetic translation is
needed. Skip the all-ones sentinel (u64::MAX on ELFCLASS64,
u32::MAX widened on ELFCLASS32) the kernel uses to mark kernel
virtual-only mappings (vmalloc, modules) that have no physical
backing. Skip zero-length segments defensively.

Pulled the translation out of kcore() into a free-standing
physical_ranges_from_segments helper so it can be unit-tested
without /proc/kcore.

find_kcore_blocks is unchanged: the iomem ∩ PT_LOAD intersection
that bounds acquisition to actual System RAM is preserved.

This addresses the same bug as #114 without that PR's regressions
(dropping the iomem cross-check, deleting the unit test, and the
silent zero-byte acquisition failure mode of the found_first gate).

Added five unit tests:

  • physical_ranges_use_paddr_not_vaddr — segments with garbage
    p_vaddr relative to p_paddr still produce correct output. This
    is the regression PPC64 hits; the old code would have mistranslated.
  • physical_ranges_skip_non_pt_load_segments — PT_NOTE etc. are
    filtered out.
  • physical_ranges_skip_sentinel_paddrs — both the 64-bit and
    zero-extended 32-bit sentinels are filtered.
  • physical_ranges_skip_zero_size_segments — defensive.
  • physical_ranges_sorted_by_paddr — output sort order is what
    find_kcore_blocks requires.

Verified with:

  • cargo clippy --all-features --all-targets -- -D warnings
  • cargo clippy --no-default-features --all-targets -- -D warnings
  • cargo test --all-features
  • cargo fmt --check

`kcore()` previously computed each segment's physical address as
`phdr.p_vaddr - (first_phdr.p_vaddr - first_iomem.start)`. That
assumes a single global virtual-to-physical offset across the entire
kernel address space -- true on x86_64 (one direct map) but not on
PPC64 or any architecture where the kernel maps physical memory
through multiple non-contiguous virtual slabs. On those systems AVML
read from the wrong file offsets, producing corrupt acquisitions.

Use `phdr.p_paddr` directly. The kernel already populates it with the
physical address backing each PT_LOAD; no arithmetic translation is
needed. Skip the all-ones sentinel (`u64::MAX` on ELFCLASS64,
`u32::MAX` widened on ELFCLASS32) the kernel uses to mark kernel
virtual-only mappings (vmalloc, modules) that have no physical
backing. Skip zero-length segments defensively.

Pulled the translation out of `kcore()` into a free-standing
`physical_ranges_from_segments` helper so it can be unit-tested
without /proc/kcore.

`find_kcore_blocks` is unchanged: the iomem ⋂ PT_LOAD intersection
that bounds acquisition to actual System RAM is preserved.

Added five unit tests:

- `physical_ranges_use_paddr_not_vaddr` -- segments with garbage
  `p_vaddr` relative to `p_paddr` still produce correct output. This
  is the regression PPC64 hits; the old code would have mistranslated.
- `physical_ranges_skip_non_pt_load_segments` -- PT_NOTE etc. are
  filtered out.
- `physical_ranges_skip_sentinel_paddrs` -- both the 64-bit and
  zero-extended 32-bit sentinels are filtered.
- `physical_ranges_skip_zero_size_segments` -- defensive.
- `physical_ranges_sorted_by_paddr` -- output sort order is what
  `find_kcore_blocks` requires.

Verified with:

- `cargo clippy --all-features --all-targets -- -D warnings`
- `cargo clippy --no-default-features --all-targets -- -D warnings`
- `cargo test --all-features`
- `cargo fmt --check`
@demoray demoray enabled auto-merge (squash) May 18, 2026 17:18
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.

1 participant