x86/MCE/AMD: Fix memory leak when threshold_create_bank() fails
authorAmmar Faizi <ammarfaizi2@gnuweeb.org>
Tue, 29 Mar 2022 10:47:05 +0000 (17:47 +0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Jun 2022 08:22:29 +0000 (10:22 +0200)
commit e5f28623ceb103e13fc3d7bd45edf9818b227fd0 upstream.

In mce_threshold_create_device(), if threshold_create_bank() fails, the
previously allocated threshold banks array @bp will be leaked because
the call to mce_threshold_remove_device() will not free it.

This happens because mce_threshold_remove_device() fetches the pointer
through the threshold_banks per-CPU variable but bp is written there
only after the bank creation is successful, and not before, when
threshold_create_bank() fails.

Add a helper which unwinds all the bank creation work previously done
and pass into it the previously allocated threshold banks array for
freeing.

  [ bp: Massage. ]

Fixes: 6458de97fc15 ("x86/mce/amd: Straighten CPU hotplug path")
Co-developed-by: Alviro Iskandar Setiawan <alviro.iskandar@gnuweeb.org>
Signed-off-by: Alviro Iskandar Setiawan <alviro.iskandar@gnuweeb.org>
Co-developed-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Ammar Faizi <ammarfaizi2@gnuweeb.org>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20220329104705.65256-3-ammarfaizi2@gnuweeb.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/kernel/cpu/mce/amd.c

index c0c57bd05f02dfc807bc3aa27e2310a9ec4412cb..a873577e49dccc28c7626e2a5b7397072eef20e1 100644 (file)
@@ -1470,10 +1470,23 @@ out_free:
        kfree(bank);
 }
 
+static void __threshold_remove_device(struct threshold_bank **bp)
+{
+       unsigned int bank, numbanks = this_cpu_read(mce_num_banks);
+
+       for (bank = 0; bank < numbanks; bank++) {
+               if (!bp[bank])
+                       continue;
+
+               threshold_remove_bank(bp[bank]);
+               bp[bank] = NULL;
+       }
+       kfree(bp);
+}
+
 int mce_threshold_remove_device(unsigned int cpu)
 {
        struct threshold_bank **bp = this_cpu_read(threshold_banks);
-       unsigned int bank, numbanks = this_cpu_read(mce_num_banks);
 
        if (!bp)
                return 0;
@@ -1484,13 +1497,7 @@ int mce_threshold_remove_device(unsigned int cpu)
         */
        this_cpu_write(threshold_banks, NULL);
 
-       for (bank = 0; bank < numbanks; bank++) {
-               if (bp[bank]) {
-                       threshold_remove_bank(bp[bank]);
-                       bp[bank] = NULL;
-               }
-       }
-       kfree(bp);
+       __threshold_remove_device(bp);
        return 0;
 }
 
@@ -1527,15 +1534,14 @@ int mce_threshold_create_device(unsigned int cpu)
                if (!(this_cpu_read(bank_map) & (1 << bank)))
                        continue;
                err = threshold_create_bank(bp, cpu, bank);
-               if (err)
-                       goto out_err;
+               if (err) {
+                       __threshold_remove_device(bp);
+                       return err;
+               }
        }
        this_cpu_write(threshold_banks, bp);
 
        if (thresholding_irq_en)
                mce_threshold_vector = amd_threshold_interrupt;
        return 0;
-out_err:
-       mce_threshold_remove_device(cpu);
-       return err;
 }