// Copyright (c) Meta Platforms, Inc. and affiliates. // SPDX-License-Identifier: LGPL-2.1-or-later 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; prog->vmcoreinfo.raw_size = descsz; prog->vmcoreinfo.raw = memdup(desc, descsz); if (!prog->vmcoreinfo.raw) return &drgn_enomem; 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 "LENGTH(mem_section)"@ err = parse_vmcoreinfo_u64(value, newline, 0, &prog->vmcoreinfo.mem_section_length); 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(phys_base)"@ { err = parse_vmcoreinfo_u64(value, newline, 0, &prog->vmcoreinfo.phys_base);; if (err) return err; break; } @case "NUMBER(KERNELPACMASK)"@ err = parse_vmcoreinfo_u64(value, newline, 16, &prog->aarch64_insn_pac_mask); if (err) return err; break; @case "NUMBER(VA_BITS)"@ err = parse_vmcoreinfo_u64(value, newline, 0, &prog->vmcoreinfo.va_bits); 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 (!is_power_of_two(prog->vmcoreinfo.page_size)) { return drgn_error_create(DRGN_ERROR_OTHER, "VMCOREINFO does not contain valid PAGESIZE"); } prog->vmcoreinfo.page_shift = ctz(prog->vmcoreinfo.page_size); if (!prog->vmcoreinfo.swapper_pg_dir) { return drgn_error_create(DRGN_ERROR_OTHER, "VMCOREINFO does not contain valid swapper_pg_dir"); } // Everything else is optional. return NULL; }