Skip to content

Commit

Permalink
BACKPORT: list: Split list_add() debug checking into separate function
Browse files Browse the repository at this point in the history
(cherry-picked from d7c816733d501b59dbdc2483f2cc8e4431fd9160)

Right now, __list_add() code is repeated either in list.h or in
list_debug.c, but the only differences between the two versions
are the debug checks. This commit therefore extracts these debug
checks into a separate __list_add_valid() function and consolidates
__list_add(). Additionally this new __list_add_valid() function will stop
list manipulations if a corruption is detected, instead of allowing for
further corruption that may lead to even worse conditions.

This is slight refactoring of the same hardening done in PaX and Grsecurity.

Change-Id: Ib2cdcf961fed22766ff4a6d2872634fc805fd633
Signed-off-by: Kees Cook <keescook@chromium.org>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Rik van Riel <riel@redhat.com>
Signed-off-by: Satya Tangirala <satyat@google.com>
Signed-off-by: khusika <khusikadhamar@gmail.com>
  • Loading branch information
kees authored and khusika committed Oct 26, 2018
1 parent 62b0795 commit 174280d
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 31 deletions.
22 changes: 16 additions & 6 deletions include/linux/list.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,37 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
list->prev = list;
}

#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#else
static inline bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
return true;
}
#endif

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
#ifndef CONFIG_DEBUG_LIST
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
if (!__list_add_valid(new, prev, next))
return;

next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif

/**
* list_add - add a new entry
Expand Down
48 changes: 23 additions & 25 deletions lib/list_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Copyright 2006, Red Hat, Inc., Dave Jones
* Released under the General Public License (GPL).
*
* This file contains the linked list implementations for
* DEBUG_LIST.
* This file contains the linked list validation for DEBUG_LIST.
*/

#include <linux/export.h>
Expand All @@ -13,33 +12,32 @@
#include <linux/rculist.h>

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
* Check that the data structures for the list manipulations are reasonably
* valid. Failures here indicate memory corruption (and possibly an exploit
* attempt).
*/

void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
bool __list_add_valid(struct list_head *new, struct list_head *prev,
struct list_head *next)
{
WARN(next->prev != prev,
"list_add corruption. next->prev should be "
"prev (%p), but was %p. (next=%p).\n",
prev, next->prev, next);
WARN(prev->next != next,
"list_add corruption. prev->next should be "
"next (%p), but was %p. (prev=%p).\n",
next, prev->next, prev);
WARN(new == prev || new == next,
"list_add double add: new=%p, prev=%p, next=%p.\n",
new, prev, next);
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
if (unlikely(next->prev != prev)) {
WARN(1, "list_add corruption. next->prev should be prev (%p), but was %p. (next=%p).\n",
prev, next->prev, next);
return false;
}
if (unlikely(prev->next != next)) {
WARN(1, "list_add corruption. prev->next should be next (%p), but was %p. (prev=%p).\n",
next, prev->next, prev);
return false;
}
if (unlikely(new == prev || new == next)) {
WARN(1, "list_add double add: new=%p, prev=%p, next=%p.\n",
new, prev, next);
return false;
}
return true;
}
EXPORT_SYMBOL(__list_add);
EXPORT_SYMBOL(__list_add_valid);

void __list_del_entry(struct list_head *entry)
{
Expand Down

0 comments on commit 174280d

Please sign in to comment.