Skip to content

Commit 02912ca

Browse files
committed
libdrgn: fix handling of p_filesz < p_memsz in core dumps
I implemented the case of a segment in a core file with p_filesz < p_memsz by treating the difference as zero bytes. This is correct for ET_EXEC and ET_DYN, but for ET_CORE, it actually means that the memory existed in the program but was not saved. For userspace core dumps, this typically happens for read-only file mappings. For kernel core dumps, makedumpfile does this to indicate memory that was excluded. Instead, let's return a DRGN_FAULT_ERROR if an attempt is made to read from these bytes. In the future, we need to read from the executable/library files when we can. Signed-off-by: Omar Sandoval <[email protected]>
1 parent 844d828 commit 02912ca

File tree

5 files changed

+40
-20
lines changed

5 files changed

+40
-20
lines changed

libdrgn/debug_info.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1183,6 +1183,12 @@ struct drgn_error *drgn_debug_info_load(struct drgn_debug_info *dbinfo,
11831183
if (err)
11841184
goto err;
11851185

1186+
/*
1187+
* TODO: for core dumps, we need to add memory reader segments for
1188+
* read-only segments of the loaded binaries since those aren't saved in
1189+
* the core dump.
1190+
*/
1191+
11861192
/*
11871193
* If this fails, it's too late to roll back. This can only fail with
11881194
* enomem, so it's not a big deal.

libdrgn/memory_reader.c

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -249,21 +249,19 @@ struct drgn_error *drgn_read_memory_file(void *buf, uint64_t address,
249249
void *arg, bool physical)
250250
{
251251
struct drgn_memory_file_segment *file_segment = arg;
252-
char *p = buf;
253-
uint64_t file_offset = file_segment->file_offset + offset;
254-
size_t file_count;
255252

256-
if (offset < file_segment->file_size) {
257-
file_count = min((uint64_t)count,
258-
file_segment->file_size - offset);
259-
count -= file_count;
260-
} else {
261-
file_count = 0;
253+
if (offset > file_segment->file_size ||
254+
count > file_segment->file_size - offset) {
255+
if (offset <= file_segment->file_size)
256+
address += file_segment->file_size - offset;
257+
return drgn_error_create_fault("memory not saved in core dump",
258+
address);
262259
}
263-
while (file_count) {
264-
ssize_t ret;
265260

266-
ret = pread(file_segment->fd, p, file_count, file_offset);
261+
uint64_t file_offset = file_segment->file_offset + offset;
262+
char *p = buf;
263+
while (count) {
264+
ssize_t ret = pread(file_segment->fd, p, count, file_offset);
267265
if (ret == -1) {
268266
if (errno == EINTR) {
269267
continue;
@@ -278,9 +276,9 @@ struct drgn_error *drgn_read_memory_file(void *buf, uint64_t address,
278276
address);
279277
}
280278
p += ret;
281-
file_count -= ret;
279+
address += ret;
280+
count -= ret;
282281
file_offset += ret;
283282
}
284-
memset(p, 0, count);
285283
return NULL;
286284
}

libdrgn/memory_reader.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,9 @@ struct drgn_memory_file_segment {
123123
uint64_t file_offset;
124124
/**
125125
* Size of the segment in the file. This may be less than the size of
126-
* the segment in memory, in which case the remaining bytes are treated
127-
* as if they contained zeroes.
126+
* the segment in memory, which means that the remaining bytes were in
127+
* the program's memory but were not saved in the core dump. Attempting
128+
* to read these bytes is treated as a fault.
128129
*/
129130
uint64_t file_size;
130131
/** File descriptor. */

libdrgn/program.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,10 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
350350
goto out_platform;
351351
}
352352

353-
if ((is_proc_kcore || vmcoreinfo_note) &&
354-
prog->platform.arch->linux_kernel_pgtable_iterator_next) {
353+
bool pgtable_reader =
354+
(is_proc_kcore || vmcoreinfo_note) &&
355+
prog->platform.arch->linux_kernel_pgtable_iterator_next;
356+
if (pgtable_reader) {
355357
/*
356358
* Try to read any memory that isn't in the core dump via the
357359
* page table.
@@ -381,6 +383,13 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
381383
prog->file_segments[j].fd = prog->core_fd;
382384
prog->file_segments[j].eio_is_fault = false;
383385
err = drgn_program_add_memory_segment(prog, phdr->p_vaddr,
386+
/*
387+
* Don't override the page
388+
* table reader for
389+
* unsaved regions.
390+
*/
391+
pgtable_reader ?
392+
phdr->p_filesz :
384393
phdr->p_memsz,
385394
drgn_read_memory_file,
386395
&prog->file_segments[j],
@@ -391,6 +400,8 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
391400
phdr->p_paddr != (is_64_bit ? UINT64_MAX : UINT32_MAX)) {
392401
err = drgn_program_add_memory_segment(prog,
393402
phdr->p_paddr,
403+
pgtable_reader ?
404+
phdr->p_filesz :
394405
phdr->p_memsz,
395406
drgn_read_memory_file,
396407
&prog->file_segments[j],
@@ -436,6 +447,8 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
436447
phys_addr = phdr->p_vaddr - direct_mapping;
437448
err = drgn_program_add_memory_segment(prog,
438449
phys_addr,
450+
pgtable_reader ?
451+
phdr->p_filesz :
439452
phdr->p_memsz,
440453
drgn_read_memory_file,
441454
&prog->file_segments[j],

tests/test_program.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ def test_physical(self):
806806
self.assertEqual(prog.read(0xFFFF0000, len(data)), data)
807807
self.assertEqual(prog.read(0xA0, len(data), physical=True), data)
808808

809-
def test_zero_fill(self):
809+
def test_unsaved(self):
810810
data = b"hello, world"
811811
prog = Program()
812812
with tempfile.NamedTemporaryFile() as f:
@@ -825,4 +825,6 @@ def test_zero_fill(self):
825825
)
826826
f.flush()
827827
prog.set_core_dump(f.name)
828-
self.assertEqual(prog.read(0xFFFF0000, len(data) + 4), data + bytes(4))
828+
with self.assertRaisesRegex(FaultError, "memory not saved in core dump") as cm:
829+
prog.read(0xFFFF0000, len(data) + 4)
830+
self.assertEqual(cm.exception.address, 0xFFFF000C)

0 commit comments

Comments
 (0)