Skip to content

Commit 24c2503

Browse files
suryasaimadhuIngo Molnar
authored and
Ingo Molnar
committed
x86/microcode: Do not access the initrd after it has been freed
When we look for microcode blobs, we first try builtin and if that doesn't succeed, we fallback to the initrd supplied to the kernel. However, at some point doing boot, that initrd gets jettisoned and we shouldn't access it anymore. But we do, as the below KASAN report shows. That's because find_microcode_in_initrd() doesn't check whether the initrd is still valid or not. So do that. ================================================================== BUG: KASAN: use-after-free in find_cpio_data Read of size 1 by task swapper/1/0 page:ffffea0000db9d40 count:0 mapcount:0 mapping: (null) index:0x1 flags: 0x100000000000000() raw: 0100000000000000 0000000000000000 0000000000000001 00000000ffffffff raw: dead000000000100 dead000000000200 0000000000000000 0000000000000000 page dumped because: kasan: bad access detected CPU: 1 PID: 0 Comm: swapper/1 Tainted: G W 4.10.0-rc5-debug-00075-g2dbde22 #3 Hardware name: Dell Inc. XPS 13 9360/0839Y6, BIOS 1.2.3 12/01/2016 Call Trace: dump_stack ? _atomic_dec_and_lock ? __dump_page kasan_report_error ? pointer ? find_cpio_data __asan_report_load1_noabort ? find_cpio_data find_cpio_data ? vsprintf ? dump_stack ? get_ucode_user ? print_usage_bug find_microcode_in_initrd __load_ucode_intel ? collect_cpu_info_early ? debug_check_no_locks_freed load_ucode_intel_ap ? collect_cpu_info ? trace_hardirqs_on ? flat_send_IPI_mask_allbutself load_ucode_ap ? get_builtin_firmware ? flush_tlb_func ? do_raw_spin_trylock ? cpumask_weight cpu_init ? trace_hardirqs_off ? play_dead_common ? native_play_dead ? hlt_play_dead ? syscall_init ? arch_cpu_idle_dead ? do_idle start_secondary start_cpu Memory state around the buggy address: ffff880036e74f00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff880036e74f80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff >ffff880036e75000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ^ ffff880036e75080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ffff880036e75100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ================================================================== Reported-by: Andrey Ryabinin <[email protected]> Tested-by: Andrey Ryabinin <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent dffba9a commit 24c2503

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

arch/x86/include/asm/microcode.h

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ extern void __init load_ucode_bsp(void);
140140
extern void load_ucode_ap(void);
141141
void reload_early_microcode(void);
142142
extern bool get_builtin_firmware(struct cpio_data *cd, const char *name);
143+
extern bool initrd_gone;
143144
#else
144145
static inline int __init microcode_init(void) { return 0; };
145146
static inline void __init load_ucode_bsp(void) { }

arch/x86/kernel/cpu/microcode/amd.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,9 @@ void load_ucode_amd_ap(unsigned int family)
384384
reget:
385385
if (!get_builtin_microcode(&cp, family)) {
386386
#ifdef CONFIG_BLK_DEV_INITRD
387-
cp = find_cpio_data(ucode_path, (void *)initrd_start,
388-
initrd_end - initrd_start, NULL);
387+
if (!initrd_gone)
388+
cp = find_cpio_data(ucode_path, (void *)initrd_start,
389+
initrd_end - initrd_start, NULL);
389390
#endif
390391
if (!(cp.data && cp.size)) {
391392
/*

arch/x86/kernel/cpu/microcode/core.c

+17-5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
static struct microcode_ops *microcode_ops;
4747
static bool dis_ucode_ldr = true;
4848

49+
bool initrd_gone;
50+
4951
LIST_HEAD(microcode_cache);
5052

5153
/*
@@ -190,21 +192,24 @@ void load_ucode_ap(void)
190192
static int __init save_microcode_in_initrd(void)
191193
{
192194
struct cpuinfo_x86 *c = &boot_cpu_data;
195+
int ret = -EINVAL;
193196

194197
switch (c->x86_vendor) {
195198
case X86_VENDOR_INTEL:
196199
if (c->x86 >= 6)
197-
return save_microcode_in_initrd_intel();
200+
ret = save_microcode_in_initrd_intel();
198201
break;
199202
case X86_VENDOR_AMD:
200203
if (c->x86 >= 0x10)
201-
return save_microcode_in_initrd_amd(c->x86);
204+
ret = save_microcode_in_initrd_amd(c->x86);
202205
break;
203206
default:
204207
break;
205208
}
206209

207-
return -EINVAL;
210+
initrd_gone = true;
211+
212+
return ret;
208213
}
209214

210215
struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa)
@@ -247,9 +252,16 @@ struct cpio_data find_microcode_in_initrd(const char *path, bool use_pa)
247252
* has the virtual address of the beginning of the initrd. It also
248253
* possibly relocates the ramdisk. In either case, initrd_start contains
249254
* the updated address so use that instead.
255+
*
256+
* initrd_gone is for the hotplug case where we've thrown out initrd
257+
* already.
250258
*/
251-
if (!use_pa && initrd_start)
252-
start = initrd_start;
259+
if (!use_pa) {
260+
if (initrd_gone)
261+
return (struct cpio_data){ NULL, 0, "" };
262+
if (initrd_start)
263+
start = initrd_start;
264+
}
253265

254266
return find_cpio_data(path, (void *)start, size, NULL);
255267
#else /* !CONFIG_BLK_DEV_INITRD */

0 commit comments

Comments
 (0)