kern/119307: TRASHIT macro blasts list header if REMOVE invoked with header ref

Doug Moore dougm at ironport.com
Thu Jan 3 08:30:04 PST 2008


>Number:         119307
>Category:       kern
>Synopsis:       TRASHIT macro blasts list header if REMOVE invoked with header ref
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jan 03 16:30:03 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Doug Moore <dougm at ironport.com>
>Release:        FreeBSD 7.0-CURRENT i386
>Organization:
>Environment:
System: FreeBSD adsl-216-63-78-19.dsl.hstntx.swbell.net 7.0-CURRENT FreeBSD 7.0-CURRENT #5: Sat Sep 15 00:42:55 CDT 2007 root at adsl-216-63-78-19.dsl.hstntx.swbell.net:/usr/src/sys/i386/compile/ATHEROS i386


	
>Description:
	A line like
	TAILQ_REMOVE(&a->tailq, a->tailq.tqh_first, next);
	will corrupt the tailq in question if QUEUE_MACRO_DEBUG is on,
	since after the first tailq item is removed, it is the *new*
	first tailq item that gets its prev and next pointers trashed.
	With QUEUE_MACRO_DEBUG off, it removes the first tailq as
	expected.  Therefore, turning on QUEUE_MACRO_DEBUG corrupts
	the tailq.
>How-To-Repeat:
	Write a self-referential invocation of a removal from tailq,
	list, etc, and add a couple of items to the list before
	invoking that removal.
>Fix:
	Use typeof to bind the value of the to-be-removed element to a
	name, and use that name in the pointer trashing and other
	debugging code.  If typeof is forbidden, then it would seem to
	require adding type arguments to several macros, a bad thing.

--- queue.h.diff begins here ---
--- queue.h	2008-01-02 22:53:54.000000000 -0600
+++ queue.h.new	2008-01-02 23:02:28.000000000 -0600
@@ -188,6 +188,7 @@ struct {								\
 #define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
 
 #define	SLIST_REMOVE(head, elm, type, field) do {			\
+	typeof (elm) oldelm = (elm);					\
 	if (SLIST_FIRST((head)) == (elm)) {				\
 		SLIST_REMOVE_HEAD((head), field);			\
 	}								\
@@ -198,7 +199,7 @@ struct {								\
 		SLIST_NEXT(curelm, field) =				\
 		    SLIST_NEXT(SLIST_NEXT(curelm, field), field);	\
 	}								\
-	TRASHIT((elm)->field.sle_next);					\
+	TRASHIT(oldelm->field.sle_next);					\
 } while (0)
 
 #define	SLIST_REMOVE_HEAD(head, field) do {				\
@@ -280,6 +281,7 @@ struct {								\
 #define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
 
 #define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	typeof (elm) oldelm = (elm);					\
 	if (STAILQ_FIRST((head)) == (elm)) {				\
 		STAILQ_REMOVE_HEAD((head), field);			\
 	}								\
@@ -291,7 +293,7 @@ struct {								\
 		     STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
 			(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
 	}								\
-	TRASHIT((elm)->field.stqe_next);				\
+	TRASHIT(oldelm->field.stqe_next);				\
 } while (0)
 
 #define	STAILQ_REMOVE_HEAD(head, field) do {				\
@@ -392,14 +394,15 @@ struct {								\
 #define	LIST_NEXT(elm, field)	((elm)->field.le_next)
 
 #define	LIST_REMOVE(elm, field) do {					\
+	typeof (elm) oldelm = (elm);					\
 	QMD_LIST_CHECK_NEXT(elm, field);				\
 	QMD_LIST_CHECK_PREV(elm, field);				\
 	if (LIST_NEXT((elm), field) != NULL)				\
 		LIST_NEXT((elm), field)->field.le_prev = 		\
 		    (elm)->field.le_prev;				\
 	*(elm)->field.le_prev = LIST_NEXT((elm), field);		\
-	TRASHIT((elm)->field.le_next);					\
-	TRASHIT((elm)->field.le_prev);					\
+	TRASHIT(oldelm->field.le_next);					\
+	TRASHIT(oldelm->field.le_prev);					\
 } while (0)
 
 /*
@@ -554,6 +557,7 @@ struct {								\
 	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
 
 #define	TAILQ_REMOVE(head, elm, field) do {				\
+	typeof (elm) oldelm = (elm);					\
 	QMD_TAILQ_CHECK_NEXT(elm, field);				\
 	QMD_TAILQ_CHECK_PREV(elm, field);				\
 	if ((TAILQ_NEXT((elm), field)) != NULL)				\
@@ -564,9 +568,9 @@ struct {								\
 		QMD_TRACE_HEAD(head);					\
 	}								\
 	*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);		\
-	TRASHIT((elm)->field.tqe_next);					\
-	TRASHIT((elm)->field.tqe_prev);					\
-	QMD_TRACE_ELEM(&(elm)->field);					\
+	TRASHIT(oldelm->field.tqe_next);				\
+	TRASHIT(oldelm->field.tqe_prev);				\
+	QMD_TRACE_ELEM(&oldelm->field);					\
 } while (0)
 
 
--- queue.h.diff ends here ---


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list