[PATCH] Add --kaslr=auto option to automatically detect kaslr offset.
This patch adds the --kaslr=auto option. When set crash will
attempt to find the aslr offset by comparing the _stext symbol
in the vmlinux file to the _stext symbol in the vmcoreinfo.
When the kernel is updated to include the kernel aslr offset
in the vmcoreinfo, that should be used instead of this indirect
method.
Signed-off-by: Andrew Honig <ahonig(a)google.com>
---
crash.8 | 7 ++++---
defs.h | 1 +
diskdump.c | 8 ++++++++
main.c | 21 +++++++++++++++------
netdump.c | 11 +++++++++++
symbols.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
6 files changed, 91 insertions(+), 14 deletions(-)
diff --git a/crash.8 b/crash.8
index 6f2c192..271bc59 100644
--- a/crash.8
+++ b/crash.8
@@ -448,11 +448,12 @@ command, search for their object files in
.I directory
instead of in the standard location.
.TP
-.BI --kaslr \ offset
-If an x86 or x86_64 kernel was configured with
+.BI --kaslr \ [offset|auto]
+If an x86_64 kernel was configured with
.B CONFIG_RANDOMIZE_BASE,
the offset value is equal to the difference between the symbol values
-compiled into the vmlinux file and their relocated value.
+compiled into the vmlinux file and their relocated value. If set to
+auto, crash will attempt to automatically calculate the kaslr offset.
.TP
.BI --reloc \ size
When analyzing live x86 kernels that were configured with a
diff --git a/defs.h b/defs.h
index c9a4b73..5f1ec9d 100644
--- a/defs.h
+++ b/defs.h
@@ -600,6 +600,7 @@ struct new_utsname {
#define PRE_KERNEL_INIT (0x20000000)
#define ARCH_PVOPS_XEN (0x40000000)
#define IRQ_DESC_TREE (0x80000000)
+#define RELOC_AUTO (0x100000000ULL)
#define GCC_VERSION_DEPRECATED (GCC_3_2|GCC_3_2_3|GCC_2_96|GCC_3_3_2|GCC_3_3_3)
diff --git a/diskdump.c b/diskdump.c
index 79fbba7..eb41744 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -845,6 +845,14 @@ is_diskdump(char *file)
get_log_from_vmcoreinfo(file, vmcoreinfo_read_string);
}
+ /*
+ * We may need the _stext_SYMBOL from the vmcore_info to adjust for
+ * kaslr and we may not have gotten it elsewhere.
+ */
+ char *tmpstring = vmcoreinfo_read_string("SYMBOL(_stext)");
+ kt->vmcoreinfo._stext_SYMBOL = htol(tmpstring, RETURN_ON_ERROR, NULL);
+ free(tmpstring);
+
return TRUE;
}
diff --git a/main.c b/main.c
index 44594f0..9fa79d7 100644
--- a/main.c
+++ b/main.c
@@ -218,13 +218,22 @@ main(int argc, char **argv)
kt->module_tree = optarg;
else if (STREQ(long_options[option_index].name, "kaslr")) {
- if (!calculate(optarg, &kt->relocate, NULL, 0)) {
- error(INFO, "invalid --kaslr argument: %s\n",
- optarg);
- program_usage(SHORT_FORM);
+ if (!machine_type("X86_64")) {
+ error(INFO, "option kaslr only valid "
+ "with X86_64 machine type.");
+ } else if (STREQ(optarg, "auto")) {
+ kt->flags |= RELOC_AUTO;
+ } else {
+ if (!calculate(optarg, &kt->relocate,
+ NULL, 0)) {
+ error(INFO,
+ "invalid --kaslr argument: %s\n",
+ optarg);
+ program_usage(SHORT_FORM);
+ }
+ kt->relocate *= -1;
+ kt->flags |= RELOC_SET;
}
- kt->relocate *= -1;
- kt->flags |= RELOC_SET;
} else if (STREQ(long_options[option_index].name, "reloc")) {
if (!calculate(optarg, &kt->relocate, NULL, 0)) {
diff --git a/netdump.c b/netdump.c
index 7dc2fca..884dd73 100644
--- a/netdump.c
+++ b/netdump.c
@@ -411,6 +411,17 @@ is_netdump(char *file, ulong source_query)
get_log_from_vmcoreinfo(file, vmcoreinfo_read_string);
}
+ /*
+ * We may need the _stext_SYMBOL from the vmcore_info to adjust for
+ * kaslr and we may not have gotten it elsewhere.
+ */
+ if (source_query == KDUMP_LOCAL) {
+ char *tmpstring = vmcoreinfo_read_string("SYMBOL(_stext)");
+ kt->vmcoreinfo._stext_SYMBOL =
+ htol(tmpstring, RETURN_ON_ERROR, NULL);
+ free(tmpstring);
+ }
+
return nd->header_size;
bailout:
diff --git a/symbols.c b/symbols.c
index 83bc0ff..7d4a4ef 100644
--- a/symbols.c
+++ b/symbols.c
@@ -557,6 +557,46 @@ strip_symbol_end(const char *name, char *buf)
}
/*
+ * Derives the kernel aslr offset by comparing the _stext symbol from the
+ * the vmcore_info in the dump file to the _stext symbol in the vmlinux file.
+ */
+static void
+derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start, bfd_byte *end,
+ unsigned int size, asymbol *store)
+{
+ symbol_info syminfo;
+ asymbol *sym;
+ char *name;
+ unsigned long relocate;
+ char buf[BUFSIZE];
+
+ if (kt->vmcoreinfo._stext_SYMBOL == 0)
+ return;
+
+ for (; start < end; start += size) {
+ sym = bfd_minisymbol_to_symbol(abfd, dynamic, start, store);
+ if (sym == NULL)
+ error(FATAL, "bfd_minisymbol_to_symbol() failed\n");
+
+ bfd_get_symbol_info(abfd, sym, &syminfo);
+ name = strip_symbol_end(syminfo.name, buf);
+ if (strcmp("_stext", name) == 0) {
+ relocate = syminfo.value - kt->vmcoreinfo._stext_SYMBOL;
+ /*
+ *To avoid mistaking an mismatched kernel version with
+ * a kaslr offset, we make sure that the offset is
+ * aligned by 0x1000, as it always will be for
+ * kaslr.
+ */
+ if ((relocate & 0xFFF) == 0) {
+ kt->relocate = relocate;
+ kt->flags |= RELOC_SET;
+ }
+ }
+ }
+}
+
+/*
* Store the symbols gathered by symtab_init(). The symbols are stored
* in increasing numerical order.
*/
@@ -591,15 +631,22 @@ store_symbols(bfd *abfd, int dynamic, void *minisyms, long
symcount,
st->symcnt = 0;
sp = st->symtable;
- if (machine_type("X86") || machine_type("X86_64")) {
- if (!(kt->flags & RELOC_SET))
+ first = 0;
+ from = (bfd_byte *) minisyms;
+ fromend = from + symcount * size;
+
+ if (machine_type("X86")) {
+ if (!(kt->flags & RELOC_SET)) {
kt->flags |= RELOC_FORCE;
+ }
+ } else if (machine_type("X86_64")) {
+ if (kt->flags & RELOC_AUTO && !(kt->flags & RELOC_SET)) {
+ derive_kaslr_offset(abfd, dynamic, from,
+ fromend, size, store);
+ }
} else
kt->flags &= ~RELOC_SET;
- first = 0;
- from = (bfd_byte *) minisyms;
- fromend = from + symcount * size;
for (; from < fromend; from += size)
{
if ((sym = bfd_minisymbol_to_symbol(abfd, dynamic, from, store))
--
1.9.0.rc1.175.g0b1dcb5