summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilly Tarreau <w@1wt.eu>2023-05-11 11:33:35 +0200
committerWilly Tarreau <w@1wt.eu>2023-05-11 11:33:35 +0200
commit29dcc5e5599e2cec236dfd3b10899dc8264e2bde (patch)
treea6d726c0ed0f84729ead63eb159c33b5f0daa62f
parent0dd4fa58e6ead1a6723a6a126f9e8b0e6979d9c3 (diff)
downloadhaproxy-29dcc5e5599e2cec236dfd3b10899dc8264e2bde.tar.gz
DEBUG: list: add DEBUG_LIST to purposely corrupt list heads after delete
LIST_DELETE doesn't affect the previous pointers of the stored element. This can sometimes hide bugs when such a pointer is reused by accident in a LIST_NEXT() or equivalent after having been detached for example, or ia another LIST_DELETE is performed again, something that LIST_DEL_INIT() is immune to. By compiling with -DDEBUG_LIST, we'll replace a freshly detached list element with two invalid pointers that will cause a crash in case of accidental misuse. It's not enabled by default.
-rw-r--r--Makefile2
-rw-r--r--include/haproxy/list.h5
2 files changed, 6 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 1f1de6d46..1151a081f 100644
--- a/Makefile
+++ b/Makefile
@@ -223,7 +223,7 @@ SMALL_OPTS =
# DEBUG_MEM_STATS, DEBUG_DONT_SHARE_POOLS, DEBUG_FD, DEBUG_POOL_INTEGRITY,
# DEBUG_NO_POOLS, DEBUG_FAIL_ALLOC, DEBUG_STRICT_ACTION=[0-3], DEBUG_HPACK,
# DEBUG_AUTH, DEBUG_SPOE, DEBUG_UAF, DEBUG_THREAD, DEBUG_STRICT, DEBUG_DEV,
-# DEBUG_TASK, DEBUG_MEMORY_POOLS, DEBUG_POOL_TRACING, DEBUG_QPACK.
+# DEBUG_TASK, DEBUG_MEMORY_POOLS, DEBUG_POOL_TRACING, DEBUG_QPACK, DEBUG_LIST.
DEBUG = -DDEBUG_STRICT -DDEBUG_MEMORY_POOLS
#### Trace options
diff --git a/include/haproxy/list.h b/include/haproxy/list.h
index dc4f7e0e1..368e6d76b 100644
--- a/include/haproxy/list.h
+++ b/include/haproxy/list.h
@@ -72,7 +72,12 @@
} while (0)
/* removes an element from a list and returns it */
+#if defined(DEBUG_LIST)
+/* purposely corrupt the detached element to detect use-after-delete */
+#define LIST_DELETE(el) ({ typeof(el) __ret = (el); (el)->n->p = (el)->p; (el)->p->n = (el)->n; *(__ret) = (struct list)ILH; (__ret);})
+#else
#define LIST_DELETE(el) ({ typeof(el) __ret = (el); (el)->n->p = (el)->p; (el)->p->n = (el)->n; (__ret); })
+#endif
/* removes an element from a list, initializes it and returns it.
* This is faster than LIST_DELETE+LIST_INIT as we avoid reloading the pointers.