bug: switch data corruption check to __must_check
authorKees Cook <keescook@chromium.org>
Fri, 24 Feb 2017 23:00:38 +0000 (15:00 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 25 Feb 2017 01:46:56 +0000 (17:46 -0800)
The CHECK_DATA_CORRUPTION() macro was designed to have callers do
something meaningful/protective on failure.  However, using "return
false" in the macro too strictly limits the design patterns of callers.
Instead, let callers handle the logic test directly, but make sure that
the result IS checked by forcing __must_check (which appears to not be
able to be used directly on macro expressions).

Link: http://lkml.kernel.org/r/20170206204547.GA125312@beast
Signed-off-by: Kees Cook <keescook@chromium.org>
Suggested-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
include/linux/bug.h
lib/list_debug.c

index baff2e8fc8a82792045c3bb0112fb52cead7e6cb..5828489309bbd22a255f11064418dc3a6b9366de 100644 (file)
@@ -124,18 +124,20 @@ static inline enum bug_trap_type report_bug(unsigned long bug_addr,
 
 /*
  * Since detected data corruption should stop operation on the affected
- * structures, this returns false if the corruption condition is found.
+ * structures. Return value must be checked and sanely acted on by caller.
  */
+static inline __must_check bool check_data_corruption(bool v) { return v; }
 #define CHECK_DATA_CORRUPTION(condition, fmt, ...)                      \
-       do {                                                             \
-               if (unlikely(condition)) {                               \
+       check_data_corruption(({                                         \
+               bool corruption = unlikely(condition);                   \
+               if (corruption) {                                        \
                        if (IS_ENABLED(CONFIG_BUG_ON_DATA_CORRUPTION)) { \
                                pr_err(fmt, ##__VA_ARGS__);              \
                                BUG();                                   \
                        } else                                           \
                                WARN(1, fmt, ##__VA_ARGS__);             \
-                       return false;                                    \
                }                                                        \
-       } while (0)
+               corruption;                                              \
+       }))
 
 #endif /* _LINUX_BUG_H */
index 7f7bfa55eb6df3e9c9c3c2a244ff02d475197e9d..a34db8d276676782ca8d45f286827e7074e26a37 100644 (file)
 bool __list_add_valid(struct list_head *new, struct list_head *prev,
                      struct list_head *next)
 {
-       CHECK_DATA_CORRUPTION(next->prev != prev,
-               "list_add corruption. next->prev should be prev (%p), but was %p. (next=%p).\n",
-               prev, next->prev, next);
-       CHECK_DATA_CORRUPTION(prev->next != next,
-               "list_add corruption. prev->next should be next (%p), but was %p. (prev=%p).\n",
-               next, prev->next, prev);
-       CHECK_DATA_CORRUPTION(new == prev || new == next,
-               "list_add double add: new=%p, prev=%p, next=%p.\n",
-               new, prev, next);
+       if (CHECK_DATA_CORRUPTION(next->prev != prev,
+                       "list_add corruption. next->prev should be prev (%p), but was %p. (next=%p).\n",
+                       prev, next->prev, next) ||
+           CHECK_DATA_CORRUPTION(prev->next != next,
+                       "list_add corruption. prev->next should be next (%p), but was %p. (prev=%p).\n",
+                       next, prev->next, prev) ||
+           CHECK_DATA_CORRUPTION(new == prev || new == next,
+                       "list_add double add: new=%p, prev=%p, next=%p.\n",
+                       new, prev, next))
+               return false;
 
        return true;
 }
@@ -41,18 +42,20 @@ bool __list_del_entry_valid(struct list_head *entry)
        prev = entry->prev;
        next = entry->next;
 
-       CHECK_DATA_CORRUPTION(next == LIST_POISON1,
-               "list_del corruption, %p->next is LIST_POISON1 (%p)\n",
-               entry, LIST_POISON1);
-       CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
-               "list_del corruption, %p->prev is LIST_POISON2 (%p)\n",
-               entry, LIST_POISON2);
-       CHECK_DATA_CORRUPTION(prev->next != entry,
-               "list_del corruption. prev->next should be %p, but was %p\n",
-               entry, prev->next);
-       CHECK_DATA_CORRUPTION(next->prev != entry,
-               "list_del corruption. next->prev should be %p, but was %p\n",
-               entry, next->prev);
+       if (CHECK_DATA_CORRUPTION(next == LIST_POISON1,
+                       "list_del corruption, %p->next is LIST_POISON1 (%p)\n",
+                       entry, LIST_POISON1) ||
+           CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
+                       "list_del corruption, %p->prev is LIST_POISON2 (%p)\n",
+                       entry, LIST_POISON2) ||
+           CHECK_DATA_CORRUPTION(prev->next != entry,
+                       "list_del corruption. prev->next should be %p, but was %p\n",
+                       entry, prev->next) ||
+           CHECK_DATA_CORRUPTION(next->prev != entry,
+                       "list_del corruption. next->prev should be %p, but was %p\n",
+                       entry, next->prev))
+               return false;
+
        return true;
 
 }