drgn/libdrgn/drgn_program_parse_vmcoreinfo.inc.strswitch
Omar Sandoval 61befc1606 libdrgn: parse AArch64 PAC mask from core dumps
In order to support removing the pointer authentication code (PAC) from
return addresses on AArch64, we need to know what bits are being used
for the PAC. We can get this from the NT_ARM_PAC_MASK note in userspace
core dumps and from the NUMBER(KERNELPACMASK) field in VMCOREINFO for
Linux kernel core dumps.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
2022-06-26 09:18:07 -07:00

92 lines
2.6 KiB
Plaintext

static struct drgn_error *parse_vmcoreinfo_u64(const char *value,
const char *newline, int base,
uint64_t *ret)
{
errno = 0;
char *end;
*ret = strtoull(value, &end, base);
if (errno == ERANGE) {
return drgn_error_create(DRGN_ERROR_OVERFLOW,
"number in VMCOREINFO is too large");
} else if (errno || end == value || end != newline) {
return drgn_error_create(DRGN_ERROR_OTHER,
"number in VMCOREINFO is invalid");
}
return NULL;
}
struct drgn_error *drgn_program_parse_vmcoreinfo(struct drgn_program *prog,
const char *desc,
size_t descsz)
{
struct drgn_error *err;
for (const char *line = desc, *end = &desc[descsz], *newline;
(newline = memchr(line, '\n', end - line));
line = newline + 1) {
const char *equals = memchr(line, '=', newline - line);
if (!equals)
continue;
const char *value = equals + 1;
@memswitch (line, equals - line)@
@case "OSRELEASE"@
if ((size_t)(newline - value) >=
sizeof(prog->vmcoreinfo.osrelease)) {
return drgn_error_create(DRGN_ERROR_OTHER,
"OSRELEASE in VMCOREINFO is too long");
}
memcpy(prog->vmcoreinfo.osrelease, value,
newline - value);
prog->vmcoreinfo.osrelease[newline - value] = '\0';
break;
@case "PAGESIZE"@
err = parse_vmcoreinfo_u64(value, newline, 0,
&prog->vmcoreinfo.page_size);
if (err)
return err;
break;
@case "KERNELOFFSET"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.kaslr_offset);
if (err)
return err;
break;
@case "SYMBOL(swapper_pg_dir)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->vmcoreinfo.swapper_pg_dir);
if (err)
return err;
break;
@case "NUMBER(pgtable_l5_enabled)"@
{
uint64_t tmp;
err = parse_vmcoreinfo_u64(value, newline, 0, &tmp);
if (err)
return err;
prog->vmcoreinfo.pgtable_l5_enabled = tmp;
break;
}
@case "NUMBER(KERNELPACMASK)"@
err = parse_vmcoreinfo_u64(value, newline, 16,
&prog->aarch64_insn_pac_mask);
if (err)
return err;
break;
@endswitch@
}
if (!prog->vmcoreinfo.osrelease[0]) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid OSRELEASE");
}
if (!prog->vmcoreinfo.page_size) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid PAGESIZE");
}
if (!prog->vmcoreinfo.swapper_pg_dir) {
return drgn_error_create(DRGN_ERROR_OTHER,
"VMCOREINFO does not contain valid swapper_pg_dir");
}
/* KERNELOFFSET, pgtable_l5_enabled, and KERNELPACMASK are optional. */
return NULL;
}