Skip to content

Commit fb107d6

Browse files
committed
check-results: strip control characters; validate the linear-map base
The script prints fields taken straight from its input, that input is untrusted — its natural source is the machine under assessment — and it runs as root. Cursor-movement and erase-line sequences therefore let a crafted results file redraw a FAIL as a PASS in the very report meant to expose it. Strip C0 controls and DEL once at ingest, before any field is extracted; newline and tab stay as the record and field separators, and bytes above 0x7f stay because they carry UTF-8. The `virt_page_offset` scalar had no validator and fell through to the "no scalar validator" skip, though it is the strongest claim on the wire: proc_kcore emits it as the exact left edge of the linear map and the engine pins PAGE_OFFSET outright from it, placing the direct map and — where text rides in the map — kernel text. Ground truth was already being derived above from the same /proc/kcore, so compare the two. The matching `V virt_page_offset` region case was an unconditional skip, which read as coverage while asserting nothing; check it per form instead, since both edges present is a window PAGE_OFFSET must fall in, a bare base is the base itself, and anything else is an address the base must sit at or below. An input with no rendered text window ended the run early with no summary. `validate_inferred` returned the status of its own failed emptiness test, which `set -e` took as a fatal error at the call site, losing the phys window check, the RAM-coverage check and the tally with it — on the very runs where the base was leaked or pinned and no window needed rendering. Report that case as a skip, like every other inapplicable check, and return an explicit status. Track three wire renames the tree already made: record type D is now R, region module_region is module_band, and scalars image_size / init_size are image_size_min / image_size_max. A case matching a token nothing emits any more passes while checking nothing.
1 parent 263fe3b commit fb107d6

1 file changed

Lines changed: 68 additions & 8 deletions

File tree

extra/check-results

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
# <type> <region>[:<name>] pos=<pos> conf=<conf> \
1717
# [lo=<hex>] [hi=<hex>|sz=<hex>] [sample=<hex>] [base_align=<hex>]
1818
#
19-
# type: P (physical), V (virtual), S (scalar-fact), D (derived)
19+
# type: P (physical), V (virtual), S (scalar-fact), R (disposition)
2020
# region: closed enum (kernel_text, kernel_data, kernel_bss,
21-
# kernel_image, module, module_region, ram, dma, dma32,
21+
# kernel_image, module, module_band, ram, dma, dma32,
2222
# initrd, cmdline, cmdline_memmap, reserved_mem, swiotlb,
2323
# vmcoreinfo, crashkernel, pmem, acpi_table, acpi_nvs,
2424
# efi_memmap, numa_node, mmio, pci_mmio, directmap,
@@ -144,7 +144,15 @@ numeric() {
144144
# Usage: validate_inferred LABEL TRUTH16 RENDERED_LINE
145145
validate_inferred() {
146146
_label="$1"; _truth="$2"; _line="$3"
147-
[ -n "$_line" ] || return # window not rendered (base leaked/pinned)
147+
# No window to check: the base was leaked or pinned outright, so nothing was
148+
# rendered to validate. Reported as a skip like every other inapplicable
149+
# check, and with an explicit status -- a bare `return` here carries the
150+
# status of the failed test above it, which under `set -e` ends the run at the
151+
# call site and takes the remaining checks and the summary with it.
152+
[ -n "$_line" ] || {
153+
skip "$_label — window not rendered (base leaked/pinned)"
154+
return 0
155+
}
148156
if [ -z "$_truth" ]; then
149157
skip "$_label — no ground truth"
150158
return
@@ -186,9 +194,21 @@ else
186194
exit 1
187195
fi
188196

197+
# SECURITY: strip C0 control characters and DEL before any value is extracted.
198+
# Every field printed below comes from this input; the input is untrusted (its
199+
# natural source is the machine under assessment); and this script runs as root.
200+
# An escape sequence reaching the terminal can move the cursor, erase the line
201+
# just written and redraw it, so a FAIL can be made to read as a PASS by the
202+
# very report meant to expose it. Newline and tab are kept -- they separate
203+
# records and fields. Bytes >= 0x80 are kept too: they carry UTF-8, and deleting
204+
# them would corrupt text rather than protect anything.
205+
# @ctrl-filter -- the same property is asserted for extra/ksymoff, which strips
206+
# control characters from its file-derived fields; review the two together.
207+
raw_input=$(printf '%s\n' "$tagged_lines" | tr -d '\000-\010\013-\037\177')
208+
189209
# Filter to tagged lines only (drops `[infer]`, `[parser]`, banner, etc.)
190-
raw_input="$tagged_lines" # full input, incl. the rendered summary lines
191-
tagged_lines=$(printf '%s\n' "$raw_input" | grep '^[VPDS] ' || true)
210+
# raw_input is the full input, incl. the rendered summary lines.
211+
tagged_lines=$(printf '%s\n' "$raw_input" | grep '^[VPS] ' || true)
192212

193213
if [ -z "$tagged_lines" ]; then
194214
echo "error: no tagged lines found" >&2
@@ -407,7 +427,7 @@ printf '%s\n' "$tagged_lines" | while IFS= read -r line; do
407427
continue
408428
fi
409429
case "$region_name" in
410-
image_size|init_size)
430+
image_size_min|image_size_max)
411431
# A kernel image (or its init region) cannot exceed the RAM it lives in.
412432
if [ "$value" = "0x0" ] || [ "$value" = "0" ]; then
413433
skip "S $region_name — zero"
@@ -481,6 +501,21 @@ printf '%s\n' "$tagged_lines" | while IFS= read -r line; do
481501
fi
482502
fi
483503
;;
504+
virt_page_offset)
505+
# The exact left edge of the linear map. Whatever emits this pins
506+
# PAGE_OFFSET outright rather than bounding it, so a wrong value
507+
# mis-places the direct map and, on the architectures where text rides
508+
# in it, kernel text with it. Ground truth is the same page_offset_base,
509+
# derived here from /proc/kcore by this script's own reader — so the
510+
# comparison is between two independent parses of the same bytes.
511+
if [ -z "$truth_page_offset" ]; then
512+
skip "S virt_page_offset=$value — no /proc/kcore page_offset_base"
513+
elif [ "$(hex16 "$value")" = "$truth_page_offset" ]; then
514+
pass "S virt_page_offset=$value — matches /proc/kcore page_offset_base"
515+
else
516+
fail "S virt_page_offset=$value — != page_offset_base 0x$truth_page_offset (/proc/kcore)"
517+
fi
518+
;;
484519
physical_start)
485520
# The real phys base sits at physical_start + KASLR offset, so the
486521
# configured minimum start can never exceed the real base.
@@ -614,7 +649,7 @@ printf '%s\n' "$tagged_lines" | while IFS= read -r line; do
614649
;;
615650

616651
# ---- Virtual: modules ----------------------------------------------
617-
"V module"|"V module_region")
652+
"V module"|"V module_band")
618653
if [ -z "$truth_mod_lo" ]; then
619654
skip "$desc — no modules loaded"
620655
continue
@@ -666,7 +701,32 @@ printf '%s\n' "$tagged_lines" | while IFS= read -r line; do
666701
;;
667702

668703
"V virt_page_offset")
669-
skip "$desc — informational (PAGE_OFFSET)"
704+
# The region names PAGE_OFFSET itself, so page_offset_base is the value
705+
# to compare against. What the record claims varies by form, and the
706+
# check follows it rather than assuming the tightest one:
707+
# both edges a window PAGE_OFFSET lies in -> truth must fall inside it
708+
# base only the base itself -> exact match
709+
# otherwise an address in the linear map -> truth is at or below it,
710+
# which also holds if the record turns out to be the base
711+
if [ -z "$truth_page_offset" ]; then
712+
skip "$desc — no /proc/kcore page_offset_base"
713+
elif [ -n "$hi" ]; then
714+
if hex_in_range "$truth_page_offset" "$addr" "$hi"; then
715+
pass "$desc — page_offset_base 0x$truth_page_offset within [0x$addr - 0x$hi]"
716+
else
717+
fail "$desc — page_offset_base 0x$truth_page_offset outside [0x$addr - 0x$hi]"
718+
fi
719+
elif [ "$pos" = "base" ]; then
720+
if [ "$addr" = "$truth_page_offset" ]; then
721+
pass "$desc — matches /proc/kcore page_offset_base"
722+
else
723+
fail "$desc — 0x$addr != page_offset_base 0x$truth_page_offset (/proc/kcore)"
724+
fi
725+
elif hex_le "$truth_page_offset" "$addr"; then
726+
pass "$desc — at/above page_offset_base 0x$truth_page_offset"
727+
else
728+
fail "$desc — 0x$addr below page_offset_base 0x$truth_page_offset (/proc/kcore)"
729+
fi
670730
;;
671731

672732
# ---- Physical: kernel image regions --------------------------------

0 commit comments

Comments
 (0)