From c36efbbea1697f5f2b85a81df7bf91af0e05cb5f Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Thu, 10 Oct 2024 16:37:26 +0200 Subject: [PATCH 1/5] tests/mkdiskdump: Fix VMCOREINFO, ELF notes and eraseinfo Store the VMCOREINFO, ELF notes and eraseinfo contents in the resulting dump file. Note that all of this data is currently stored in the sub-header. Makedumpfile also uses the sub-header for VMCOREINFO and ELF notes, but it saves eraseinfo at the end of the file, so the generated files are valid but organized slightly differently than real-world samples. Signed-off-by: Petr Tesarik --- tests/mkdiskdump.c | 117 ++++++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 38 deletions(-) diff --git a/tests/mkdiskdump.c b/tests/mkdiskdump.c index 98ebc8f4..de023d33 100644 --- a/tests/mkdiskdump.c +++ b/tests/mkdiskdump.c @@ -85,11 +85,6 @@ enum compress_method { COMPRESS_ZSTD, }; -struct data_block { - off_t filepos; - struct blob *blob; -}; - static bool flattened; static unsigned long long flattened_type = MDF_TYPE_FLAT_HEADER; static unsigned long long flattened_version = MDF_VERSION_FLAT_HEADER; @@ -123,9 +118,9 @@ static unsigned long long split; static unsigned long long start_pfn; static unsigned long long end_pfn; -static struct data_block vmcoreinfo; -static struct data_block notes; -static struct data_block eraseinfo; +static struct blob *vmcoreinfo; +static struct blob *notes; +static struct blob *eraseinfo; static char *vmcoreinfo_file; static char *note_file; @@ -251,6 +246,7 @@ writeheader_32(FILE *f) struct timeval tv; struct disk_dump_header_32 hdr; struct kdump_sub_header_32 subhdr; + off_t pos; if (gettimeofday(&tv, NULL) != 0) { perror("gettimeofday"); @@ -290,23 +286,45 @@ writeheader_32(FILE *f) if (write_chunk(f, 0, &hdr, sizeof hdr, "header")) return TEST_ERR; + pos = DISKDUMP_HEADER_BLOCKS * block_size + sizeof(subhdr); subhdr.phys_base = htodump32(be, phys_base); subhdr.dump_level = htodump32(be, dump_level); subhdr.split = htodump32(be, split); subhdr.start_pfn = htodump32(be, start_pfn); subhdr.end_pfn = htodump32(be, end_pfn); - subhdr.offset_vmcoreinfo = htodump64(be, vmcoreinfo.filepos); - subhdr.size_vmcoreinfo = htodump32(be, (vmcoreinfo.blob - ? vmcoreinfo.blob->length - : 0)); - subhdr.offset_note = htodump64(be, notes.filepos); - subhdr.size_note = htodump32(be, (notes.blob - ? notes.blob->length - : 0)); - subhdr.offset_eraseinfo = htodump64(be, eraseinfo.filepos); - subhdr.size_eraseinfo = htodump32(be, (eraseinfo.blob - ? eraseinfo.blob->length - : 0)); + if (vmcoreinfo) { + subhdr.offset_vmcoreinfo = htodump64(be, pos); + subhdr.size_vmcoreinfo = htodump32(be, vmcoreinfo->length); + if (write_chunk(f, pos, vmcoreinfo->data, vmcoreinfo->length, + "VMCOREINFO")) + return TEST_ERR; + pos += vmcoreinfo->length; + } else { + subhdr.offset_vmcoreinfo = htodump64(be, 0); + subhdr.size_vmcoreinfo = htodump32(be, 0); + } + if (notes) { + subhdr.offset_note = htodump64(be, pos); + subhdr.size_note = htodump32(be, notes->length); + if (write_chunk(f, pos, notes->data, notes->length, + "ELF notes")) + return TEST_ERR; + pos += notes->length; + } else { + subhdr.offset_note = htodump64(be, 0); + subhdr.size_note = htodump32(be, 0); + } + if (eraseinfo) { + subhdr.offset_eraseinfo = htodump64(be, pos); + subhdr.size_eraseinfo = htodump32(be, eraseinfo->length); + if (write_chunk(f, pos, notes->data, notes->length, + "eraseinfo")) + return TEST_ERR; + pos += eraseinfo->length; + } else { + subhdr.offset_eraseinfo = htodump64(be, 0); + subhdr.size_eraseinfo = htodump32(be, 0); + } subhdr.start_pfn_64 = htodump64(be, start_pfn); subhdr.end_pfn_64 = htodump64(be, end_pfn); subhdr.max_mapnr_64 = htodump64(be, max_mapnr); @@ -324,6 +342,7 @@ writeheader_64(FILE *f) struct timeval tv; struct disk_dump_header_64 hdr; struct kdump_sub_header_64 subhdr; + off_t pos; if (gettimeofday(&tv, NULL) != 0) { perror("gettimeofday"); @@ -363,23 +382,45 @@ writeheader_64(FILE *f) if (write_chunk(f, 0, &hdr, sizeof hdr, "header")) return TEST_ERR; + pos = DISKDUMP_HEADER_BLOCKS * block_size + sizeof(subhdr); subhdr.phys_base = htodump64(be, phys_base); subhdr.dump_level = htodump32(be, dump_level); subhdr.split = htodump32(be, split); subhdr.start_pfn = htodump64(be, start_pfn); subhdr.end_pfn = htodump64(be, end_pfn); - subhdr.offset_vmcoreinfo = htodump64(be, vmcoreinfo.filepos); - subhdr.size_vmcoreinfo = htodump64(be, (vmcoreinfo.blob - ? vmcoreinfo.blob->length - : 0)); - subhdr.offset_note = htodump64(be, notes.filepos); - subhdr.size_note = htodump64(be, (notes.blob - ? notes.blob->length - : 0)); - subhdr.offset_eraseinfo = htodump64(be, eraseinfo.filepos); - subhdr.size_eraseinfo = htodump64(be, (eraseinfo.blob - ? eraseinfo.blob->length - : 0)); + if (vmcoreinfo) { + subhdr.offset_vmcoreinfo = htodump64(be, pos); + subhdr.size_vmcoreinfo = htodump64(be, vmcoreinfo->length); + if (write_chunk(f, pos, vmcoreinfo->data, vmcoreinfo->length, + "VMCOREINFO")) + return TEST_ERR; + pos += vmcoreinfo->length; + } else { + subhdr.offset_vmcoreinfo = htodump64(be, 0); + subhdr.size_vmcoreinfo = htodump64(be, 0); + } + if (notes) { + subhdr.offset_note = htodump64(be, pos); + subhdr.size_note = htodump64(be, notes->length); + if (write_chunk(f, pos, notes->data, notes->length, + "ELF notes")) + return TEST_ERR; + pos += notes->length; + } else { + subhdr.offset_note = htodump64(be, 0); + subhdr.size_note = htodump64(be, 0); + } + if (eraseinfo) { + subhdr.offset_eraseinfo = htodump64(be, pos); + subhdr.size_eraseinfo = htodump64(be, eraseinfo->length); + if (write_chunk(f, pos, notes->data, notes->length, + "eraseinfo")) + return TEST_ERR; + pos += eraseinfo->length; + } else { + subhdr.offset_eraseinfo = htodump64(be, 0); + subhdr.size_eraseinfo = htodump64(be, 0); + } subhdr.start_pfn_64 = htodump64(be, start_pfn); subhdr.end_pfn_64 = htodump64(be, end_pfn); subhdr.max_mapnr_64 = htodump64(be, max_mapnr); @@ -904,20 +945,20 @@ main(int argc, char **argv) return rc; if (vmcoreinfo_file) { - vmcoreinfo.blob = slurp(vmcoreinfo_file); - if (vmcoreinfo.blob == NULL) + vmcoreinfo = slurp(vmcoreinfo_file); + if (vmcoreinfo == NULL) return TEST_ERR; } if (note_file) { - notes.blob = slurp(note_file); - if (notes.blob == NULL) + notes = slurp(note_file); + if (notes == NULL) return TEST_ERR; } if (eraseinfo_file) { - eraseinfo.blob = slurp(eraseinfo_file); - if (eraseinfo.blob == NULL) + eraseinfo = slurp(eraseinfo_file); + if (eraseinfo == NULL) return TEST_ERR; } From 5c1b16982dba300295c40a232ec22fb37498bc71 Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Thu, 10 Oct 2024 16:46:51 +0200 Subject: [PATCH 2/5] tests: Check VMCOREINFO in diskdump files Add a check for VMCOREINFO in diskdump v3 files. Signed-off-by: Petr Tesarik --- tests/Makefile.am | 1 + tests/diskdump-basic | 3 ++- tests/diskdump-basic-vmcoreinfo | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100755 tests/diskdump-basic-vmcoreinfo diff --git a/tests/Makefile.am b/tests/Makefile.am index 6b7697fe..00ce8027 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -196,6 +196,7 @@ test_scripts = \ diskdump-empty-s390x \ diskdump-empty-x86_64 \ diskdump-basic-raw \ + diskdump-basic-vmcoreinfo \ diskdump-flat-raw \ diskdump-multiread \ diskdump-excluded \ diff --git a/tests/diskdump-basic b/tests/diskdump-basic index 1f676a05..831cc93f 100644 --- a/tests/diskdump-basic +++ b/tests/diskdump-basic @@ -14,7 +14,6 @@ echo "@0 $pageflags" > "$datafile" cat "$expectfile" >> "$datafile" ./mkdiskdump "$dumpfile" < Date: Thu, 10 Oct 2024 16:51:28 +0200 Subject: [PATCH 3/5] Fix reading blob attributes from a flattened format Read blob attributes using flatmap_get_chunk() rather than fcache_get_chunk(), as the latter does not correctly find the data in a flattened file. The bug can be triggered by reading VMCOREINFO from a diskdump v3 file. Add the corresponding test case. Signed-off-by: Petr Tesarik --- src/kdumpfile/util.c | 2 +- tests/Makefile.am | 1 + tests/diskdump-flat-vmcoreinfo | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100755 tests/diskdump-flat-vmcoreinfo diff --git a/src/kdumpfile/util.c b/src/kdumpfile/util.c index d9648c15..23d698e8 100644 --- a/src/kdumpfile/util.c +++ b/src/kdumpfile/util.c @@ -1345,7 +1345,7 @@ read_blob_attr(kdump_ctx_t *ctx, unsigned fidx, off_t off, size_t size, struct fcache_chunk fch; kdump_status ret; - ret = fcache_get_chunk(ctx->shared->fcache, &fch, size, fidx, off); + ret = flatmap_get_chunk(ctx->shared->flatmap, &fch, size, fidx, off); if (ret != KDUMP_OK) return set_error(ctx, ret, "Cannot read %s (%zu bytes at %llu in %s)", diff --git a/tests/Makefile.am b/tests/Makefile.am index 00ce8027..63ae69de 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -198,6 +198,7 @@ test_scripts = \ diskdump-basic-raw \ diskdump-basic-vmcoreinfo \ diskdump-flat-raw \ + diskdump-flat-vmcoreinfo \ diskdump-multiread \ diskdump-excluded \ diskdump-split \ diff --git a/tests/diskdump-flat-vmcoreinfo b/tests/diskdump-flat-vmcoreinfo new file mode 100755 index 00000000..a85bf3b1 --- /dev/null +++ b/tests/diskdump-flat-vmcoreinfo @@ -0,0 +1,20 @@ +#! /bin/sh + +# +# Create a flattened diskdump file with VMCOREINFO data. +# See also diskdump-basic-vmcoreinfo. +# + +pageflags=raw +extraparam=" +flattened = yes +version = 3 +VMCOREINFO = vmcoreinfo.data +" +extracheckattr=" +linux.vmcoreinfo.lines.OSRELEASE = string:6.4.3-1-default +linux.vmcoreinfo.lines.PAGESIZE = string:4096 +linux.vmcoreinfo.lines.CRASHTIME = string:1689103980 +" +. "$srcdir"/diskdump-basic +exit 0 From 4cb7d32d8c626f29ec478c667507a8836d562c0f Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Thu, 10 Oct 2024 17:10:50 +0200 Subject: [PATCH 4/5] tests: Add forgotten VMCOREINFO data file The newly added test cases fail without this file... Signed-off-by: Petr Tesarik --- tests/Makefile.am | 1 + tests/vmcoreinfo.data | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/vmcoreinfo.data diff --git a/tests/Makefile.am b/tests/Makefile.am index 63ae69de..f000c188 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -394,6 +394,7 @@ dist_check_DATA = \ diskdump-split.expect.3 \ sys-xlat-x86_64-linux.expect \ sys-xlat-x86_64-linux-xen.expect \ + vmcoreinfo.data \ xlatmap.expect \ xlat-os-aarch64-none.expect \ xlat-os-ia32-none.expect \ diff --git a/tests/vmcoreinfo.data b/tests/vmcoreinfo.data new file mode 100644 index 00000000..986b6bc0 --- /dev/null +++ b/tests/vmcoreinfo.data @@ -0,0 +1,3 @@ +OSRELEASE=6.4.3-1-default +PAGESIZE=4096 +CRASHTIME=1689103980 From 764baba26d0ff69aa3eec6bfdf2b15d4d40410dc Mon Sep 17 00:00:00 2001 From: Petr Tesarik Date: Thu, 10 Oct 2024 20:03:18 +0200 Subject: [PATCH 5/5] x86_64: Fix ktext mapping if it starts above 0xffffffffa0000000 When all of kernel text is relocated beyond the pre-kASLR highest virtual address, the scan for region end is not even attempted. If the kernel text start address is found above LINUX_KTEXT_END_NOKASLR, unconditionally scan for the region end up to LINUX_KTEXT_END. The corresponding test case is derived from a real-world dump. Signed-off-by: Petr Tesarik --- src/addrxlat/x86_64.c | 7 ++-- tests/Makefile.am | 4 ++ tests/xlat-linux-x86_64-ktext-54M-kaslr | 14 +++++++ tests/xlat-linux-x86_64-ktext-54M-kaslr.data | 9 +++++ .../xlat-linux-x86_64-ktext-54M-kaslr.expect | 40 +++++++++++++++++++ tests/xlat-linux-x86_64-ktext-54M-kaslr.sym | 4 ++ 6 files changed, 75 insertions(+), 3 deletions(-) create mode 100755 tests/xlat-linux-x86_64-ktext-54M-kaslr create mode 100644 tests/xlat-linux-x86_64-ktext-54M-kaslr.data create mode 100644 tests/xlat-linux-x86_64-ktext-54M-kaslr.expect create mode 100644 tests/xlat-linux-x86_64-ktext-54M-kaslr.sym diff --git a/src/addrxlat/x86_64.c b/src/addrxlat/x86_64.c index 4aa460ca..2e11fe9b 100644 --- a/src/addrxlat/x86_64.c +++ b/src/addrxlat/x86_64.c @@ -541,9 +541,10 @@ linux_ktext_extents(struct os_init_data *ctl, linearoff = ctl->sys->meth[ADDRXLAT_SYS_METH_KTEXT].param.linear.off; *high = *low; - status = highest_linear(&step, high, LINUX_KTEXT_END_NOKASLR, - linearoff); - if (status == ADDRXLAT_OK && *high == LINUX_KTEXT_END_NOKASLR) { + if (*high <= LINUX_KTEXT_END_NOKASLR) + status = highest_linear(&step, high, LINUX_KTEXT_END_NOKASLR, + linearoff); + if (status == ADDRXLAT_OK && *high >= LINUX_KTEXT_END_NOKASLR) { ++*high; status = highest_linear(&step, high, LINUX_KTEXT_END, linearoff); diff --git a/tests/Makefile.am b/tests/Makefile.am index f000c188..e751cebf 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -297,6 +297,7 @@ test_scripts = \ xlat-linux-x86_64-ktext-40M \ xlat-linux-x86_64-ktext-512M \ xlat-linux-x86_64-ktext-520M \ + xlat-linux-x86_64-ktext-54M-kaslr \ xlat-linux-x86_64-old \ xlat-linux-x86_64-old-nover \ xlat-linux-x86_64-2.6.11 \ @@ -479,6 +480,9 @@ dist_check_DATA = \ xlat-linux-x86_64-ktext-520M.data \ xlat-linux-x86_64-ktext-520M.expect \ xlat-linux-x86_64-ktext-520M.sym \ + xlat-linux-x86_64-ktext-54M-kaslr.data \ + xlat-linux-x86_64-ktext-54M-kaslr.expect \ + xlat-linux-x86_64-ktext-54M-kaslr.sym \ xlat-linux-x86_64-old.expect \ xlat-linux-x86_64-old-nover.data \ xlat-linux-x86_64-old-nover.expect \ diff --git a/tests/xlat-linux-x86_64-ktext-54M-kaslr b/tests/xlat-linux-x86_64-ktext-54M-kaslr new file mode 100755 index 00000000..7d1596e1 --- /dev/null +++ b/tests/xlat-linux-x86_64-ktext-54M-kaslr @@ -0,0 +1,14 @@ +#! /bin/bash + +# +# Check Linux X86_64 translation with a 54M kASLR ktext mapping starting +# above the fixed (non-kASLR) virtual address region. +# + +opts=( + arch=x86_64 + ostype=linux + phys_base=0x23c00000 +) + +. "$srcdir"/xlat-os-common diff --git a/tests/xlat-linux-x86_64-ktext-54M-kaslr.data b/tests/xlat-linux-x86_64-ktext-54M-kaslr.data new file mode 100644 index 00000000..2c77ff61 --- /dev/null +++ b/tests/xlat-linux-x86_64-ktext-54M-kaslr.data @@ -0,0 +1,9 @@ +# kernel text +@0x4c610ff8 +000000004c615067 +@0x4c615ff0 +000000004c616063 +@0x4c616000 +0000000000000000*313 +000000004ae001e1*27+200000 +0000000000000000*172 diff --git a/tests/xlat-linux-x86_64-ktext-54M-kaslr.expect b/tests/xlat-linux-x86_64-ktext-54M-kaslr.expect new file mode 100644 index 00000000..b03a342f --- /dev/null +++ b/tests/xlat-linux-x86_64-ktext-54M-kaslr.expect @@ -0,0 +1,40 @@ +@rootpgt: PGT + target_as=MACHPHYSADDR + root=KVADDR:0xffffffffa8a10000 + pte_mask=0x0 + pte_format=x86_64 + fields=12,9,9,9,9 + +@ktext: LINEAR + target_as=KPHYSADDR + off=0xa3c00000 + +@machphys_kphys: LINEAR + target_as=KPHYSADDR + off=0x0 + +@kphys_machphys: LINEAR + target_as=MACHPHYSADDR + off=0x0 + +KV -> HW: +0-7fffffffffff: @rootpgt +800000000000-ffff7fffffffffff: NONE +ffff800000000000-ffffffffffffffff: @rootpgt + +KV -> PHYS: +0-7fffffffffff: @rootpgt +800000000000-ffff7fffffffffff: NONE +ffff800000000000-ffffffffa71fffff: @rootpgt +ffffffffa7200000-ffffffffaa7fffff: @ktext +ffffffffaa800000-ffffffffffffffff: @rootpgt + +KPHYS -> DIRECT: + +MACHPHYS -> KPHYS: +0-fffffffffffff: @machphys_kphys +10000000000000-ffffffffffffffff: NONE + +KPHYS -> MACHPHYS: +0-fffffffffffff: @kphys_machphys +10000000000000-ffffffffffffffff: NONE diff --git a/tests/xlat-linux-x86_64-ktext-54M-kaslr.sym b/tests/xlat-linux-x86_64-ktext-54M-kaslr.sym new file mode 100644 index 00000000..3abd9936 --- /dev/null +++ b/tests/xlat-linux-x86_64-ktext-54M-kaslr.sym @@ -0,0 +1,4 @@ +@VALUE(init_top_pgt) +ffffffffa8a10000 +@VALUE(_stext) +ffffffffa7200000