In particular, we test the corruption cases.
#include <stdlib.h>
#include "list.h"
+static void *corrupt(const char *abortstr,
+ const struct list_node *head,
+ const struct list_node *node,
+ unsigned int count)
+{
+ if (abortstr) {
+ fprintf(stderr,
+ "%s: prev corrupt in node %p (%u) of %p\n",
+ abortstr, node, count, head);
+ abort();
+ }
+ return NULL;
+}
+
struct list_node *list_check_node(const struct list_node *node,
const char *abortstr)
{
for (p = node, n = node->next; n != node; p = n, n = n->next) {
count++;
- if (n->prev != p) {
- if (!abortstr)
- return NULL;
- fprintf(stderr,
- "%s: prev corrupt in node %p (%u) of %p\n",
- abortstr, n, count, node);
- abort();
- }
+ if (n->prev != p)
+ return corrupt(abortstr, node, n, count);
}
+ /* Check prev on head node. */
+ if (node->prev != p)
+ return corrupt(abortstr, node, node, 0);
+
return (struct list_node *)node;
}
struct list_head *list_check(const struct list_head *h, const char *abortstr)
{
- if (h->n.next == &h->n) {
- if (h->n.prev != &h->n) {
- if (!abortstr)
- return NULL;
- fprintf(stderr, "%s: prev corrupt in empty %p\n",
- abortstr, h);
- abort();
- }
- return (struct list_head *)h;
- }
-
if (!list_check_node(&h->n, abortstr))
return NULL;
return (struct list_head *)h;
--- /dev/null
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <err.h>
+
+/* We don't actually want it to exit... */
+static jmp_buf aborted;
+#define abort() longjmp(aborted, 1)
+
+#define fprintf my_fprintf
+static char printf_buffer[1000];
+
+static int my_fprintf(FILE *stream, const char *format, ...)
+{
+ va_list ap;
+ int ret;
+ va_start(ap, format);
+ ret = vsprintf(printf_buffer, format, ap);
+ va_end(ap);
+ return ret;
+}
+
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+
+int main(int argc, char *argv[])
+{
+ struct list_head list;
+ struct list_node n1;
+ char expect[100];
+
+ plan_tests(9);
+ /* Empty list. */
+ list.n.next = &list.n;
+ list.n.prev = &list.n;
+ ok1(list_check(&list, NULL) == &list);
+
+ /* Bad back ptr */
+ list.n.prev = &n1;
+ /* Non-aborting version. */
+ ok1(list_check(&list, NULL) == NULL);
+
+ /* Aborting version. */
+ sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n",
+ &list, &list);
+ if (setjmp(aborted) == 0) {
+ list_check(&list, "test message");
+ fail("list_check on empty with bad back ptr didn't fail!");
+ } else {
+ ok1(strcmp(printf_buffer, expect) == 0);
+ }
+
+ /* n1 in list. */
+ list.n.next = &n1;
+ list.n.prev = &n1;
+ n1.prev = &list.n;
+ n1.next = &list.n;
+ ok1(list_check(&list, NULL) == &list);
+ ok1(list_check_node(&n1, NULL) == &n1);
+
+ /* Bad back ptr */
+ n1.prev = &n1;
+ ok1(list_check(&list, NULL) == NULL);
+ ok1(list_check_node(&n1, NULL) == NULL);
+
+ /* Aborting version. */
+ sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n",
+ &n1, &list);
+ if (setjmp(aborted) == 0) {
+ list_check(&list, "test message");
+ fail("list_check on n1 bad back ptr didn't fail!");
+ } else {
+ ok1(strcmp(printf_buffer, expect) == 0);
+ }
+
+ sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n",
+ &n1, &n1);
+ if (setjmp(aborted) == 0) {
+ list_check_node(&n1, "test message");
+ fail("list_check_node on n1 bad back ptr didn't fail!");
+ } else {
+ ok1(strcmp(printf_buffer, expect) == 0);
+ }
+
+ return exit_status();
+}
--- /dev/null
+#define CCAN_LIST_DEBUG 1
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+int main(int argc, char *argv[])
+{
+ struct list_head list1, list2;
+ struct list_node n1, n2, n3;
+ pid_t child;
+ int status;
+
+ plan_tests(1);
+ list_head_init(&list1);
+ list_head_init(&list2);
+ list_add(&list1, &n1);
+ list_add(&list2, &n2);
+ list_add_tail(&list2, &n3);
+
+ child = fork();
+ if (child) {
+ wait(&status);
+ } else {
+ /* This should abort. */
+ list_del_from(&list1, &n3);
+ exit(0);
+ }
+
+ ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT);
+ list_del_from(&list2, &n3);
+ return exit_status();
+}