[PATCH 2/2] lib: add optional linked list debugging

Ahmad Fatoum a.fatoum at pengutronix.de
Wed Jan 3 02:19:48 PST 2024


When enabled, this outputs messages like:

  list_del corruption, 000000005fe4a9d0->next is LIST_POISON1 (0000000000000100)

which can be useful when debugging.

Signed-off-by: Ahmad Fatoum <a.fatoum at pengutronix.de>
---
 common/Kconfig        |  8 +++++
 include/linux/bug.h   | 17 +++++++++++
 lib/Kconfig.hardening | 10 +++++++
 lib/Makefile          |  1 +
 lib/list_debug.c      | 68 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 104 insertions(+)
 create mode 100644 lib/list_debug.c

diff --git a/common/Kconfig b/common/Kconfig
index c8c23a8e03a2..18ad01713293 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -1704,6 +1704,14 @@ config DMA_API_DEBUG
 
 	  If unsure, say N.
 
+config DEBUG_LIST
+	bool "Debug linked list manipulation"
+	help
+	  Enable this to turn on extended checks in the linked-list
+	  walking routines.
+
+	  If unsure, say N.
+
 config PBL_BREAK
 	bool "Execute software break on pbl start"
 	depends on ARM && (!CPU_32v4T && !ARCH_TEGRA)
diff --git a/include/linux/bug.h b/include/linux/bug.h
index d8fc328a0706..8ea5f8d1b260 100644
--- a/include/linux/bug.h
+++ b/include/linux/bug.h
@@ -6,4 +6,21 @@
 #include <asm-generic/bug.h>
 #include <linux/build_bug.h>
 
+/*
+ * Since detected data corruption should stop operation on the affected
+ * 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, ...)			 \
+	check_data_corruption(({					 \
+		bool corruption = unlikely(condition);			 \
+		if (corruption) {					 \
+			if (IS_ENABLED(CONFIG_BUG_ON_DATA_CORRUPTION)) { \
+				panic(fmt, ##__VA_ARGS__);		 \
+			} else						 \
+				WARN(1, fmt, ##__VA_ARGS__);		 \
+		}							 \
+		corruption;						 \
+	}))
+
 #endif	/* _LINUX_BUG_H */
diff --git a/lib/Kconfig.hardening b/lib/Kconfig.hardening
index 7f74d0d98a9c..28be42a27465 100644
--- a/lib/Kconfig.hardening
+++ b/lib/Kconfig.hardening
@@ -1,5 +1,15 @@
 menu "Hardening options"
 
+config BUG_ON_DATA_CORRUPTION
+	bool "Trigger a BUG when data corruption is detected"
+	select DEBUG_LIST
+	help
+	  Select this option if barebox should BUG when it encounters
+	  data corruption in its memory structures when they get checked
+	  for validity.
+
+	  If unsure, say N.
+
 config STACK_GUARD_PAGE
 	bool "Place guard page to catch stack overflows"
 	depends on ARM && MMU
diff --git a/lib/Makefile b/lib/Makefile
index 38204c8273e5..853d8870fe14 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -71,6 +71,7 @@ obj-$(CONFIG_FONTS)	+= fonts/
 obj-$(CONFIG_BAREBOX_LOGO)     += logo/
 obj-y			+= reed_solomon/
 obj-$(CONFIG_RATP)	+= ratp.o
+obj-$(CONFIG_DEBUG_LIST) += list_debug.o
 obj-y			+= list_sort.o
 obj-y			+= refcount.o
 obj-y			+= int_sqrt.o
diff --git a/lib/list_debug.c b/lib/list_debug.c
new file mode 100644
index 000000000000..7de4c709a391
--- /dev/null
+++ b/lib/list_debug.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2006, Red Hat, Inc., Dave Jones
+ * Released under the General Public License (GPL).
+ *
+ * This file contains the linked list validation for DEBUG_LIST.
+ */
+
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+/*
+ * Check that the data structures for the list manipulations are reasonably
+ * valid. Failures here indicate memory corruption (and possibly an exploit
+ * attempt).
+ */
+
+bool __list_add_valid_or_report(struct list_head *new, struct list_head *prev,
+				struct list_head *next)
+{
+	if (CHECK_DATA_CORRUPTION(prev == NULL,
+			"list_add corruption. prev is NULL.\n") ||
+	    CHECK_DATA_CORRUPTION(next == NULL,
+			"list_add corruption. next is NULL.\n") ||
+	    CHECK_DATA_CORRUPTION(next->prev != prev,
+			"list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n",
+			prev, next->prev, next) ||
+	    CHECK_DATA_CORRUPTION(prev->next != next,
+			"list_add corruption. prev->next should be next (%px), but was %px. (prev=%px).\n",
+			next, prev->next, prev) ||
+	    CHECK_DATA_CORRUPTION(new == prev || new == next,
+			"list_add double add: new=%px, prev=%px, next=%px.\n",
+			new, prev, next))
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL(__list_add_valid_or_report);
+
+bool __list_del_entry_valid_or_report(struct list_head *entry)
+{
+	struct list_head *prev, *next;
+
+	prev = entry->prev;
+	next = entry->next;
+
+	if (CHECK_DATA_CORRUPTION(next == NULL,
+			"list_del corruption, %px->next is NULL\n", entry) ||
+	    CHECK_DATA_CORRUPTION(prev == NULL,
+			"list_del corruption, %px->prev is NULL\n", entry) ||
+	    CHECK_DATA_CORRUPTION(next == LIST_POISON1,
+			"list_del corruption, %px->next is LIST_POISON1 (%px)\n",
+			entry, LIST_POISON1) ||
+	    CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
+			"list_del corruption, %px->prev is LIST_POISON2 (%px)\n",
+			entry, LIST_POISON2) ||
+	    CHECK_DATA_CORRUPTION(prev->next != entry,
+			"list_del corruption. prev->next should be %px, but was %px. (prev=%px)\n",
+			entry, prev->next, prev) ||
+	    CHECK_DATA_CORRUPTION(next->prev != entry,
+			"list_del corruption. next->prev should be %px, but was %px. (next=%px)\n",
+			entry, next->prev, next))
+		return false;
+
+	return true;
+}
+EXPORT_SYMBOL(__list_del_entry_valid_or_report);
-- 
2.39.2




More information about the barebox mailing list