Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

pstore/ftrace: Factor KASLR offset in the core kernel instruction addresses

The pstore ftrace frontend works by purely collecting the
instruction address, saving it on the persistent area through
the backend and when the log is read, on next boot for example,
the address is then resolved by using the regular printk symbol
lookup (%pS for example).

Problem: if we are running a relocatable kernel with KASLR enabled,
this is a recipe for failure in the symbol resolution on next boots,
since the addresses are offset'ed by the KASLR address. So, naturally
the way to go is factor the KASLR address out of instruction address
collection, and adding the fresh offset when resolving the symbol
on future boots.

Problem #2: modules also have varying addresses that float based
on module base address and potentially the module ordering in
memory, meaning factoring KASLR offset for them is useless.
So, let's hereby only take KASLR offset into account for core
kernel addresses, leaving module ones as is.

And we have yet a 3rd complexity: not necessarily the check range
for core kernel addresses holds true on future boots, since the
module base address will vary. With that, the choice was to mark
the addresses as being core vs module based on its MSB. And with
that...

...we have the 4th challenge here: for some "simple" architectures,
the CPU number is saved bit-encoded on the instruction pointer, to
allow bigger timestamps - this is set through the PSTORE_CPU_IN_IP
define for such architectures. Hence, the approach here is to skip
such architectures (at least in a first moment).

Finished? No. On top of all previous complexities, we have one
extra pain point: kaslr_offset() is inlined and fully "resolved"
at boot-time, after kernel decompression, through ELF relocation
mechanism. Once the offset is known, it's patched to the kernel
text area, wherever it is used. The mechanism, and its users, are
only built-in - incompatible with module usage. Though there are
possibly some hacks (as computing the offset using some kallsym
lookup), the choice here is to restrict this optimization to the
(hopefully common) case of CONFIG_PSTORE=y.

TL;DR: let's factor KASLR offsets on pstore/ftrace for core kernel
addresses, only when PSTORE is built-in and leaving module addresses
out, as well as architectures that define PSTORE_CPU_IN_IP.

Signed-off-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
Link: https://patch.msgid.link/20260410205848.2607169-1-gpiccoli@igalia.com
Signed-off-by: Kees Cook <kees@kernel.org>

authored by

Guilherme G. Piccoli and committed by
Kees Cook
24b8f8dc 421a41c4

+32 -4
+26 -2
fs/pstore/ftrace.c
··· 18 18 #include <linux/cache.h> 19 19 #include <linux/slab.h> 20 20 #include <asm/barrier.h> 21 + #include <asm/setup.h> 21 22 #include "internal.h" 22 23 23 24 /* This doesn't need to be atomic: speed is chosen over correctness here. */ 24 25 static u64 pstore_ftrace_stamp; 26 + 27 + static inline unsigned long adjust_ip(unsigned long ip) 28 + { 29 + #if defined(CONFIG_RANDOMIZE_BASE) && !defined(PSTORE_CPU_IN_IP) && IS_BUILTIN(CONFIG_PSTORE) 30 + if (core_kernel_text(ip)) 31 + return ip - kaslr_offset(); 32 + 33 + __clear_bit(BITS_PER_LONG - 1, &ip); 34 + #endif 35 + return ip; 36 + } 37 + 38 + inline unsigned long decode_ip(unsigned long ip) 39 + { 40 + #if defined(CONFIG_RANDOMIZE_BASE) && !defined(PSTORE_CPU_IN_IP) && IS_BUILTIN(CONFIG_PSTORE) 41 + if (test_bit(BITS_PER_LONG - 1, &ip)) 42 + return ip + kaslr_offset(); 43 + 44 + __set_bit(BITS_PER_LONG - 1, &ip); 45 + 46 + #endif 47 + return ip; 48 + } 25 49 26 50 static void notrace pstore_ftrace_call(unsigned long ip, 27 51 unsigned long parent_ip, ··· 71 47 72 48 local_irq_save(flags); 73 49 74 - rec.ip = ip; 75 - rec.parent_ip = parent_ip; 50 + rec.ip = adjust_ip(ip); 51 + rec.parent_ip = adjust_ip(parent_ip); 76 52 pstore_ftrace_write_timestamp(&rec, pstore_ftrace_stamp++); 77 53 pstore_ftrace_encode_cpu(&rec, raw_smp_processor_id()); 78 54 psinfo->write(&record);
+4 -2
fs/pstore/inode.c
··· 105 105 struct pstore_private *ps = s->private; 106 106 struct pstore_ftrace_seq_data *data = v; 107 107 struct pstore_ftrace_record *rec; 108 + unsigned long ip, parent_ip; 108 109 109 110 if (!data) 110 111 return 0; 111 112 112 113 rec = (struct pstore_ftrace_record *)(ps->record->buf + data->off); 113 114 115 + ip = decode_ip(rec->ip); 116 + parent_ip = decode_ip(rec->parent_ip); 114 117 seq_printf(s, "CPU:%d ts:%llu %08lx %08lx %ps <- %pS\n", 115 118 pstore_ftrace_decode_cpu(rec), 116 119 pstore_ftrace_read_timestamp(rec), 117 - rec->ip, rec->parent_ip, (void *)rec->ip, 118 - (void *)rec->parent_ip); 120 + ip, parent_ip, (void *)ip, (void *)parent_ip); 119 121 120 122 return 0; 121 123 }
+2
fs/pstore/internal.h
··· 9 9 extern unsigned int kmsg_bytes; 10 10 11 11 #ifdef CONFIG_PSTORE_FTRACE 12 + extern unsigned long decode_ip(unsigned long ip); 12 13 extern void pstore_register_ftrace(void); 13 14 extern void pstore_unregister_ftrace(void); 14 15 ssize_t pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size, ··· 17 16 #else 18 17 static inline void pstore_register_ftrace(void) {} 19 18 static inline void pstore_unregister_ftrace(void) {} 19 + static inline unsigned long decode_ip(unsigned long ip) { return ip; } 20 20 static inline ssize_t 21 21 pstore_ftrace_combine_log(char **dest_log, size_t *dest_log_size, 22 22 const char *src_log, size_t src_log_size)