Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7c81673 authored by Kees Cook's avatar Kees Cook Committed by Paul E. McKenney
Browse files

list: Split list_add() debug checking into separate function



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.

Signed-off-by: default avatarKees Cook <keescook@chromium.org>
Acked-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: default avatarRik van Riel <riel@redhat.com>
parent 1001354c
Loading
Loading
Loading
Loading
+16 −6
Original line number Diff line number Diff line
@@ -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;
	WRITE_ONCE(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
+23 −25
Original line number Diff line number Diff line
@@ -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>
@@ -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,
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",
	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);
	WARN(prev->next != next,
		"list_add corruption. prev->next should be "
		"next (%p), but was %p. (prev=%p).\n",
		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);
	WARN(new == prev || new == next,
	     "list_add double add: new=%p, prev=%p, next=%p.\n",
		return false;
	}
	if (unlikely(new == prev || new == next)) {
		WARN(1, "list_add double add: new=%p, prev=%p, next=%p.\n",
			new, prev, next);
	next->prev = new;
	new->next = next;
	new->prev = prev;
	WRITE_ONCE(prev->next, new);
		return false;
	}
	return true;
}
EXPORT_SYMBOL(__list_add);
EXPORT_SYMBOL(__list_add_valid);

void __list_del_entry(struct list_head *entry)
{