x86/mce/amd: Publish the bank pointer only after setup has succeeded
authorBorislav Petkov <bp@suse.de>
Tue, 4 Feb 2020 12:28:41 +0000 (13:28 +0100)
committerBorislav Petkov <bp@suse.de>
Thu, 13 Feb 2020 17:58:39 +0000 (18:58 +0100)
threshold_create_bank() creates a bank descriptor per MCA error
thresholding counter which can be controlled over sysfs. It publishes
the pointer to that bank in a per-CPU variable and then goes on to
create additional thresholding blocks if the bank has such.

However, that creation of additional blocks in
allocate_threshold_blocks() can fail, leading to a use-after-free
through the per-CPU pointer.

Therefore, publish that pointer only after all blocks have been setup
successfully.

Fixes: 019f34fccfd5 ("x86, MCE, AMD: Move shared bank to node descriptor")
Reported-by: Saar Amar <Saar.Amar@microsoft.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Cc: <stable@vger.kernel.org>
Link: http://lkml.kernel.org/r/20200128140846.phctkvx5btiexvbx@kili.mountain
arch/x86/kernel/cpu/mce/amd.c

index b3a50d962851cec722df10623dbc90e6a3f16985..e7313e5c497cccaf23db52331e5f4611482b0a9d 100644 (file)
@@ -1198,8 +1198,9 @@ static const char *get_name(unsigned int bank, struct threshold_block *b)
        return buf_mcatype;
 }
 
-static int allocate_threshold_blocks(unsigned int cpu, unsigned int bank,
-                                    unsigned int block, u32 address)
+static int allocate_threshold_blocks(unsigned int cpu, struct threshold_bank *tb,
+                                    unsigned int bank, unsigned int block,
+                                    u32 address)
 {
        struct threshold_block *b = NULL;
        u32 low, high;
@@ -1243,16 +1244,12 @@ static int allocate_threshold_blocks(unsigned int cpu, unsigned int bank,
 
        INIT_LIST_HEAD(&b->miscj);
 
-       if (per_cpu(threshold_banks, cpu)[bank]->blocks) {
-               list_add(&b->miscj,
-                        &per_cpu(threshold_banks, cpu)[bank]->blocks->miscj);
-       } else {
-               per_cpu(threshold_banks, cpu)[bank]->blocks = b;
-       }
+       if (tb->blocks)
+               list_add(&b->miscj, &tb->blocks->miscj);
+       else
+               tb->blocks = b;
 
-       err = kobject_init_and_add(&b->kobj, &threshold_ktype,
-                                  per_cpu(threshold_banks, cpu)[bank]->kobj,
-                                  get_name(bank, b));
+       err = kobject_init_and_add(&b->kobj, &threshold_ktype, tb->kobj, get_name(bank, b));
        if (err)
                goto out_free;
 recurse:
@@ -1260,7 +1257,7 @@ recurse:
        if (!address)
                return 0;
 
-       err = allocate_threshold_blocks(cpu, bank, block, address);
+       err = allocate_threshold_blocks(cpu, tb, bank, block, address);
        if (err)
                goto out_free;
 
@@ -1345,8 +1342,6 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank)
                goto out_free;
        }
 
-       per_cpu(threshold_banks, cpu)[bank] = b;
-
        if (is_shared_bank(bank)) {
                refcount_set(&b->cpus, 1);
 
@@ -1357,9 +1352,13 @@ static int threshold_create_bank(unsigned int cpu, unsigned int bank)
                }
        }
 
-       err = allocate_threshold_blocks(cpu, bank, 0, msr_ops.misc(bank));
-       if (!err)
-               goto out;
+       err = allocate_threshold_blocks(cpu, b, bank, 0, msr_ops.misc(bank));
+       if (err)
+               goto out_free;
+
+       per_cpu(threshold_banks, cpu)[bank] = b;
+
+       return 0;
 
  out_free:
        kfree(b);