x86/microcode/AMD: Free unneeded patch before exit from update_cache()
authorShu Wang <shuwang@redhat.com>
Mon, 24 Jul 2017 10:12:26 +0000 (12:12 +0200)
committerIngo Molnar <mingo@kernel.org>
Tue, 25 Jul 2017 09:26:19 +0000 (11:26 +0200)
verify_and_add_patch() allocates memory for a microcode patch and hands
it down to be added to the cache of patches. However, if the cache
already has the latest patch, the newly allocated one needs to be freed
before returning. Do that.

This issue has been found by kmemleak:

  unreferenced object 0xffff88010e780b40 (size 32):
    comm "bash", pid 860, jiffies 4294690939 (age 29.297s)
    backtrace:
       kmemleak_alloc
       kmem_cache_alloc_trace
       load_microcode_amd.isra.0
       request_microcode_amd
       reload_store
       dev_attr_store
       sysfs_kf_write
       kernfs_fop_write
       __vfs_write
       vfs_write
       SyS_write
       do_syscall_64
       return_from_SYSCALL_64
       0xffffffffffffffff

  (gdb) list *0xffffffff81050d60
  0xffffffff81050d60 is in load_microcode_amd
                (arch/x86/kernel/cpu/microcode/amd.c:616).

which is this:

patch = kzalloc(sizeof(*patch), GFP_KERNEL);
--> if (!patch) {
pr_err("Patch allocation failure.\n");
return -EINVAL;
}

Signed-off-by: Shu Wang <shuwang@redhat.com>
[ Rewrite commit message. ]
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: chuhu@redhat.com
Cc: liwang@redhat.com
Link: http://lkml.kernel.org/r/20170724101228.17326-2-bp@alien8.de
Signed-off-by: Ingo Molnar <mingo@kernel.org>
arch/x86/kernel/cpu/microcode/amd.c

index 21b1857..c6daec4 100644 (file)
@@ -400,9 +400,12 @@ static void update_cache(struct ucode_patch *new_patch)
 
        list_for_each_entry(p, &microcode_cache, plist) {
                if (p->equiv_cpu == new_patch->equiv_cpu) {
-                       if (p->patch_id >= new_patch->patch_id)
+                       if (p->patch_id >= new_patch->patch_id) {
                                /* we already have the latest patch */
+                               kfree(new_patch->data);
+                               kfree(new_patch);
                                return;
+                       }
 
                        list_replace(&p->plist, &new_patch->plist);
                        kfree(p->data);