Re: memory corruptor in .18rc1-git

Dave Jones
2006-07-14T04:31:29Z

On Fri, Jul 14, 2006 at 06:25:25AM +0200, Willy Tarreau wrote:
> > > Then you might consider slightly changing the debug messages, because they
> > > are identical in list_add and list_del. Having a way to differenciate
> > > between the two functions might give one more indication.
> >
> > BUG() gives a line number.
>
> oops! sorry, I did not notice it just after the printk(). Next time I will
> not post before coffee :-)

Though in the case I hit this morning, the text from the BUG() wouldn't
have made it over serial. But then, unless the first few chars were different,
not much else would have either. It didn't even complete the printk.

For most cases though, it should suffice. It gives enough clues that
'something stomped on memory' in cases like I saw, and in other cases
where the box still survives, it'll be possible to get the full context
from dmesg. For info, here's a cleaned up variant of the patch with
some suggestions from Linus. Andrew also merged an earlier variant into -mm.

Dave

diff --git a/include/linux/list.h b/include/linux/list.h
index 6b74adf..26e5abc 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -57,10 +57,15 @@ static inline void __list_add(struct lis
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
+#ifndef CONFIG_DEBUG_LIST
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
+#else
+extern void list_add(struct list_head *new, struct list_head *head);
+#endif
+

/**
* list_add_tail - add a new entry
@@ -153,12 +158,16 @@ static inline void __list_del(struct lis
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
+#ifndef CONFIG_DEBUG_LIST
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
+#else
+extern void list_del(struct list_head *entry);
+#endif

/**
* list_del_rcu - deletes entry from list without re-initialization
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index e5889b1..bb17ce3 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -311,6 +311,15 @@ config DEBUG_VM

If unsure, say N.

+config DEBUG_LIST
+ bool "Debug linked list manipulation"
+ depends on DEBUG_KERNEL
+ help
+ Enable this to turn on extended checks in the linked-list
+ walking routines.
+
+ If unsure, say N.
+
config FRAME_POINTER
bool "Compile the kernel with frame pointers"
depends on DEBUG_KERNEL && (X86 || CRIS || M68K || M68KNOMMU || FRV || UML || S390)
diff --git a/lib/Makefile b/lib/Makefile
index be9719a..7d7da98 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -28,6 +28,7 @@ lib-$(CONFIG_GENERIC_HWEIGHT) += hweight
obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o
obj-$(CONFIG_PLIST) += plist.o
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
+obj-$(CONFIG_DEBUG_LIST) += list_debug.o

ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
lib-y += dec_and_lock.o
diff --git a/lib/list_debug.c b/lib/list_debug.c
new file mode 100644
index 0000000..28b2d46
--- /dev/null
+++ b/lib/list_debug.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2006, Red Hat, Inc., Dave Jones
+ * Released under the General Public License (GPL).
+ *
+ * This file contains the linked list implementations for
+ * DEBUG_LIST.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+void list_add(struct list_head *new, struct list_head *head)
+{
+ if (head->next->prev != head) {
+ printk("List corruption. next->prev should be %p, but was %p\n",
+ head, head->next->prev);
+ BUG();
+ }
+ if (head->prev->next != head) {
+ printk("List corruption. prev->next should be %p, but was %p\n",
+ head, head->prev->next);
+ BUG();
+ }
+
+ __list_add(new, head, head->next);
+}
+EXPORT_SYMBOL(list_add);
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+void list_del(struct list_head *entry)
+{
+ if (entry->prev->next != entry) {
+ printk("List corruption. prev->next should be %p, but was %p\n",
+ entry, entry->prev->next);
+ BUG();
+ }
+ if (entry->next->prev != entry) {
+ printk("List corruption. next->prev should be %p, but was %p\n",
+ entry, entry->next->prev);
+ BUG();
+ }
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+EXPORT_SYMBOL(list_del);
+