powerpc/powernv/memtrace: Fix crashing the kernel when enabling concurrently
authorDavid Hildenbrand <david@redhat.com>
Wed, 11 Nov 2020 14:53:16 +0000 (15:53 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Dec 2020 10:54:16 +0000 (11:54 +0100)
commit d6718941a2767fb383e105d257d2105fe4f15f0e upstream.

It's very easy to crash the kernel right now by simply trying to
enable memtrace concurrently, hammering on the "enable" interface

loop.sh:
  #!/bin/bash

  dmesg --console-off

  while true; do
          echo 0x40000000 > /sys/kernel/debug/powerpc/memtrace/enable
  done

[root@localhost ~]# loop.sh &
[root@localhost ~]# loop.sh &

Resulting quickly in a kernel crash. Let's properly protect using a
mutex.

Fixes: 9d5171a8f248 ("powerpc/powernv: Enable removal of memory for in memory tracing")
Cc: stable@vger.kernel.org# v4.14+
Signed-off-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Oscar Salvador <osalvador@suse.de>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20201111145322.15793-3-david@redhat.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/powerpc/platforms/powernv/memtrace.c

index eea1f94..0e42fe2 100644 (file)
@@ -30,6 +30,7 @@ struct memtrace_entry {
        char name[16];
 };
 
+static DEFINE_MUTEX(memtrace_mutex);
 static u64 memtrace_size;
 
 static struct memtrace_entry *memtrace_array;
@@ -279,6 +280,7 @@ static int memtrace_online(void)
 
 static int memtrace_enable_set(void *data, u64 val)
 {
+       int rc = -EAGAIN;
        u64 bytes;
 
        /*
@@ -291,25 +293,31 @@ static int memtrace_enable_set(void *data, u64 val)
                return -EINVAL;
        }
 
+       mutex_lock(&memtrace_mutex);
+
        /* Re-add/online previously removed/offlined memory */
        if (memtrace_size) {
                if (memtrace_online())
-                       return -EAGAIN;
+                       goto out_unlock;
        }
 
-       if (!val)
-               return 0;
+       if (!val) {
+               rc = 0;
+               goto out_unlock;
+       }
 
        /* Offline and remove memory */
        if (memtrace_init_regions_runtime(val))
-               return -EINVAL;
+               goto out_unlock;
 
        if (memtrace_init_debugfs())
-               return -EINVAL;
+               goto out_unlock;
 
        memtrace_size = val;
-
-       return 0;
+       rc = 0;
+out_unlock:
+       mutex_unlock(&memtrace_mutex);
+       return rc;
 }
 
 static int memtrace_enable_get(void *data, u64 *val)