convert DM printk macros to pr_<level> macros & fix printk() rate limiting code & fix race between dm_get_from_kobject() and __dm_destroy() in kernel-dm CVE-2017-18203

Tracked by Jolla

asked 2018-06-26 07:59:53 +0300

this post is marked as community wiki

This post is a wiki. Anyone with karma >75 is welcome to improve it.

updated 2018-06-26 07:59:53 +0300

lpr gravatar image

The dm_get_from_kobject function in drivers/md/dm.c in the Linux kernel before 4.14.3 allow local users to cause a denial of service (BUG) by leveraging a race condition with __dm_destroy during creation and removal of DM devices. CVSS Score: 4.7medium local

The jolla1 has following Kernel-options set, so DM is present: CONFIG_MD=y , CONFIG_BLK_DEV_DM_BUILTIN=y , CONFIG_BLK_DEV_DM=y and CONFIG_DM_CRYPT=y.

Kernel-3.2 patches are available: |1| |2| |3|

Files affected: kernel-adaptation-sbj-3.4.108.20171107.1/drivers/md/dm.c

kernel-adaptation-sbj-3.4.108.20171107.1/include/linux/device-mapper.h

So the whole patch should look like:

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index c00bcdc..ca688a2 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -25,16 +25,6 @@

 #define DM_MSG_PREFIX "core"

-#ifdef CONFIG_PRINTK
-/*
- * ratelimit state to be used in DMXXX_LIMIT().
- */
-DEFINE_RATELIMIT_STATE(dm_ratelimit_state,
-              DEFAULT_RATELIMIT_INTERVAL,
-              DEFAULT_RATELIMIT_BURST);
-EXPORT_SYMBOL(dm_ratelimit_state);
-#endif
-
 /*
  * Cookies are numeric values sent with CHANGE and REMOVE
  * uevents while resuming, removing or renaming the device.

diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index ca688a2..d7e6399 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2692,11 +2682,15 @@ struct mapped_device *dm_get_from_kobject(struct kobject *kobj)

md = container_of(kobj, struct mapped_device, kobj_holder.kobj);

-   if (test_bit(DMF_FREEING, &md->flags) ||
-       dm_deleting_md(md))
-       return NULL;
-
+   spin_lock(&_minor_lock);
+   if (test_bit(DMF_FREEING, &md->flags) || dm_deleting_md(md)) {
+       md = NULL;
+       goto out;
+   }
dm_get(md);
+out:
+   spin_unlock(&_minor_lock);
+
return md;
 }

diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 213a870..e1e1842 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -404,48 +404,41 @@ extern struct ratelimit_state dm_ratelimit_state;
 #define dm_ratelimit() 0
 #endif

-#define DMCRIT(f, arg...) \
-   printk(KERN_CRIT DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-
-#define DMERR(f, arg...) \
-   printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMERR_LIMIT(f, arg...) \
-   do { \
-       if (dm_ratelimit()) \
-           printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " \
-                  f "\n", ## arg); \
-   } while (0)
-
-#define DMWARN(f, arg...) \
-   printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMWARN_LIMIT(f, arg...) \
-   do { \
-       if (dm_ratelimit()) \
-           printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " \
-                  f "\n", ## arg); \
-   } while (0)
-
-#define DMINFO(f, arg...) \
-   printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
-#define DMINFO_LIMIT(f, arg...) \
-   do { \
-       if (dm_ratelimit()) \
-           printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f \
-                  "\n", ## arg); \
-   } while (0)
+#define DM_FMT(fmt) DM_NAME ": " DM_MSG_PREFIX ": " fmt "\n"
+
+#define DMCRIT(fmt, ...) pr_crit(DM_FMT(fmt), ##__VA_ARGS__)
+
+#define DMERR(fmt, ...) pr_err(DM_FMT(fmt), ##__VA_ARGS__)
+#define DMERR_LIMIT(fmt, ...)                      \
+do {                                   \
+   if (dm_ratelimit())                     \
+       DMERR(fmt, ##__VA_ARGS__);              \
+} while (0)
+
+#define DMWARN(fmt, ...) pr_warn(DM_FMT(fmt), ##__VA_ARGS__)
+#define DMWARN_LIMIT(fmt, ...)                     \
+do {                                   \
+   if (dm_ratelimit())                     \
+       DMWARN(fmt, ##__VA_ARGS__);             \
+} while (0)
+
+#define DMINFO(fmt, ...) pr_info(DM_FMT(fmt), ##__VA_ARGS__)
+#define DMINFO_LIMIT(fmt, ...)                     \
+do {                                   \
+   if (dm_ratelimit())                     \
+       DMINFO(fmt, ##__VA_ARGS__);             \
+} while (0)

 #ifdef CONFIG_DM_DEBUG
-#  define DMDEBUG(f, arg...) \
-   printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX " DEBUG: " f "\n", ## arg)
-#  define DMDEBUG_LIMIT(f, arg...) \
-   do { \
-       if (dm_ratelimit()) \
-           printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX ": " f \
-                  "\n", ## arg); \
-   } while (0)
+#define DMDEBUG(fmt, ...) printk(KERN_DEBUG DM_FMT(fmt), ##__VA_ARGS__)
+#define DMDEBUG_LIMIT(fmt, ...)                        \
+do {                                   \
+   if (dm_ratelimit())                     \
+       DMDEBUG(fmt, ##__VA_ARGS__);                \
+} while (0)
 #else
-#  define DMDEBUG(f, arg...) do {} while (0)
-#  define DMDEBUG_LIMIT(f, arg...) do {} while (0)
+#define DMDEBUG(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
+#define DMDEBUG_LIMIT(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
 #endif

 #define DMEMIT(x...) sz += ((sz >= maxlen) ? \

and as a second step, finally:

diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index e1e1842..45bf2dd 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -396,46 xxxxx @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size);
  *---------------------------------------------------------------*/
 #define DM_NAME "device-mapper"

-#ifdef CONFIG_PRINTK
-extern struct ratelimit_state dm_ratelimit_state;
-
-#define dm_ratelimit() __ratelimit(&dm_ratelimit_state)
-#else
-#define dm_ratelimit() 0
-#endif
+#define DM_RATELIMIT(pr_func, fmt, ...)                    \
+do {                                   \
+   static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL,   \
+                     DEFAULT_RATELIMIT_BURST);     \
+                                   \
+   if (__ratelimit(&rs))                       \
+       pr_func(DM_FMT(fmt), ##__VA_ARGS__);            \
+} while (0)

 #define DM_FMT(fmt) DM_NAME ": " DM_MSG_PREFIX ": " fmt "\n"

 #define DMCRIT(fmt, ...) pr_crit(DM_FMT(fmt), ##__VA_ARGS__)

 #define DMERR(fmt, ...) pr_err(DM_FMT(fmt), ##__VA_ARGS__)
-#define DMERR_LIMIT(fmt, ...)                      \
-do {                                   \
-   if (dm_ratelimit())                     \
-       DMERR(fmt, ##__VA_ARGS__);              \
-} while (0)
-
+#define DMERR_LIMIT(fmt, ...) DM_RATELIMIT(pr_err, fmt, ##__VA_ARGS__)
 #define DMWARN(fmt, ...) pr_warn(DM_FMT(fmt), ##__VA_ARGS__)
-#define DMWARN_LIMIT(fmt, ...)                     \
-do {                                   \
-   if (dm_ratelimit())                     \
-       DMWARN(fmt, ##__VA_ARGS__);             \
-} while (0)
-
+#define DMWARN_LIMIT(fmt, ...) DM_RATELIMIT(pr_warn, fmt, ##__VA_ARGS__)
 #define DMINFO(fmt, ...) pr_info(DM_FMT(fmt), ##__VA_ARGS__)
-#define DMINFO_LIMIT(fmt, ...)                     \
-do {                                   \
-   if (dm_ratelimit())                     \
-       DMINFO(fmt, ##__VA_ARGS__);             \
-} while (0)
+#define DMINFO_LIMIT(fmt, ...) DM_RATELIMIT(pr_info, fmt, ##__VA_ARGS__)

 #ifdef CONFIG_DM_DEBUG
 #define DMDEBUG(fmt, ...) printk(KERN_DEBUG DM_FMT(fmt), ##__VA_ARGS__)
-#define DMDEBUG_LIMIT(fmt, ...)                        \
-do {                                   \
-   if (dm_ratelimit())                     \
-       DMDEBUG(fmt, ##__VA_ARGS__);                \
-} while (0)
+#define DMDEBUG_LIMIT(fmt, ...) DM_RATELIMIT(pr_debug, fmt, ##__VA_ARGS__)
 #else
 #define DMDEBUG(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
 #define DMDEBUG_LIMIT(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
edit retag flag offensive close delete