Coverity 抱怨双重免费、潜在的误报?

Coverity complaining about double-free, potential false positive?

我正在使用 Coverity 检查一些使用 TAILQ 的代码。它抱怨几个我不太明白的潜在的双重释放和释放后使用。代码总是类似于这样:

#include <stdlib.h>
typedef struct data {
  char file[64];
} data_t;

typedef struct dat {
  TAILQ_ENTRY(dat) d_link;
  data_t *data;
} dat_t;

TAILQ_HEAD(data_queue, dat);
struct data_queue dataq;

data_t* data_get_first(struct data_queue *q)
{
  dat_t *d;
  data_t *data;

  d = TAILQ_FIRST(q);
  if (d) {
    data = d->data;
    TAILQ_REMOVE(q, d, d_link);
    free(d);
    return data;
  }
  return NULL;
}

void loopq() {
    data_t* data;
    while((data = data_get_first(&dataq)) != NULL) {
        ...
    }
}

Coverity 告诉我,在 while 循环期间,可能会发生 tqh_first(因此队列的第一个元素)被释放并返回,然后在下一个 运行 中再次被释放.我对 TAILQ 的使用是否正确,或者我是否需要以某种方式确保 tqh_first 在我删除最后一个元素后更新为 NULL(因此它不会再次被释放)?删除代码似乎没有这样做(或者至少对我来说并不明显where/how):

#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)

#define TAILQ_REMOVE(head, elm, field) do {             \
    if ((TAILQ_NEXT((elm), field)) != NULL)             \
        TAILQ_NEXT((elm), field)->field.tqe_prev =      \
            (elm)->field.tqe_prev;              \
    else                                \
        (head)->tqh_last = (elm)->field.tqe_prev;       \
    *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);      \
} while (0)

#define TAILQ_FIRST(head)   ((head)->tqh_first)

#define TAILQ_HEAD(name, type)                      \
struct name {                               \
    struct type *tqh_first; /* first element */         \
    struct type **tqh_last; /* addr of last next element */     \
}

#define TAILQ_ENTRY(type)                       \
struct {                                \
    struct type *tqe_next;  /* next element */          \
    struct type **tqe_prev; /* address of previous next element */  \
}

我现在将定义修改得更详细一点以便于测试:

#define TAILQ_REMOVE(head, elm, field) do {             \
    if ((TAILQ_NEXT((elm), field)) != NULL) {             \
        printf("Next != NULL\n"); \
        printf("Setting next (0x%x) to prev (0x%x)\n", TAILQ_NEXT((elm), field)->field.tqe_prev, (elm)->field.tqe_prev); \
        TAILQ_NEXT((elm), field)->field.tqe_prev =      \
            (elm)->field.tqe_prev;              \
    } else {                                \
        printf("Next == NULL\n"); \
        printf("Setting last (0x%x) to prev (0x%x), first is at 0x%x\n", (head)->tqh_last, (elm)->field.tqe_prev, (head)->tqh_first); \
        (head)->tqh_last = (elm)->field.tqe_prev;       \
    } \
    printf("Setting prev (0x%x) to next (0x%x)\n", *(elm)->field.tqe_prev, TAILQ_NEXT((elm), field)); \
    *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);      \
} while (0)

并且还打印了 TAILQ_FIRST returned/what 分配给 d 的内容,输出为:

Got element 0x50e80260
Next == NULL
Setting last (0x50e80260) to prev (0x4f0ad020), first is at 0x50e80260
Setting prev (0x50e80260) to next (0x0)
Got element 0x0

看来它正在正确更新列表,而这是 Coverity 的误报?

编辑: 我在第一部分中所说的大部分内容都是早期的猜测,在我进行了下面的全面测试后可能无法成立。我觉得有些指针是“双星”这一事实让我有点吃惊。我已经把它移到了底部以供参考。

我注意到你在做:

struct data_queue dataq;

但是,应该是:

struct data_queue dataq = TAILQ_HEAD_INITIALIZER(dataq);

或者,您是否在其他地方做同样的事情?

反正定义是:

#define TAILQ_HEAD_INITIALIZER(head)                    \
    { NULL, &(head).tqh_first }

所以,最初 tqh_last 指向它自己(例如 tqh_first)。

从您上次调试 运行,根据您的评论:

I think I finally understand a little of what's going on. I updated the question with a little more debug output now. To me it looks like a false positive now. The crucial line seems to be *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); which will set first to null when the last element is removed.

是的,因为在那一点上,tqe_prev 指向 tqh_first,就像在 TAILQ_HEAD_INITIALIZER

之后一样

因此,代码最终可以正常工作。但是,诚然,这有点棘手。静态分析器很容易被这个搞糊涂。也就是说,它不承认 tqe_prev 可以 指向 &q.tqh_first 如果 元素是第一个列表。

您可以尝试使用 -fsanitize=address 进行编译。如果存在类似于 coverity 正在检查的问题,那应该会动态地告诉您。

您没有 post 处理 data_t 结构的完整代码,因此代码可能在释放或损坏后使用。


由于我的错误推测,我添加了一堆调试代码来检查不匹配,UB等

虽然在很大程度上矫枉过正,但这里是:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>

/*
 * Tail queue definitions.
 */

// 1=add guard areas to structs
#ifndef GUARD
#define GUARD       1
#endif

// add debug print to macros (OP's debug printf)
#ifndef DEBUG
#define DEBUG       1
#endif

#if GUARD
#define HEAD_RSVP_MAX       4
#define HEAD_RSVP   void *tqh_rsvp[HEAD_RSVP_MAX];

#define ELE_RSVP_MAX        2
#define ELE_RSVP    void *tqe_rsvp[ELE_RSVP_MAX];
#else
#define HEAD_RSVP   /**/
#define ELE_RSVP    /**/
#endif

#define _TAILQ_HEAD(name, type, qual)                   \
struct name {                               \
    qual type *tqh_first;       /* first element */     \
    qual type *qual *tqh_last;  /* addr of last next element */ \
    HEAD_RSVP \
}
#define TAILQ_HEAD(name, type)  _TAILQ_HEAD(name, struct type,)

#define TAILQ_HEAD_INITIALIZER(head)                    \
    { NULL, &(head).tqh_first }

#define _TAILQ_ENTRY(type, qual)                    \
struct {                                \
    ELE_RSVP \
    qual type *tqe_next;        /* next element */      \
    qual type *qual *tqe_prev;  /* address of previous next element */\
}
#define TAILQ_ENTRY(type)   _TAILQ_ENTRY(struct type,)

/*
 * Tail queue functions.
 */
#define TAILQ_INIT(head) do {                       \
    (head)->tqh_first = NULL;                   \
    (head)->tqh_last = &(head)->tqh_first;              \
} while (/*CONSTCOND*/0)

#define TAILQ_INSERT_HEAD(head, elm, field) do {            \
    if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)    \
        (head)->tqh_first->field.tqe_prev =         \
            &(elm)->field.tqe_next;             \
    else                                \
        (head)->tqh_last = &(elm)->field.tqe_next;      \
    (head)->tqh_first = (elm);                  \
    (elm)->field.tqe_prev = &(head)->tqh_first;         \
} while (/*CONSTCOND*/0)

#define TAILQ_INSERT_TAIL(head, elm, field) do {            \
    (elm)->field.tqe_next = NULL;                   \
    (elm)->field.tqe_prev = (head)->tqh_last;           \
    *(head)->tqh_last = (elm);                  \
    (head)->tqh_last = &(elm)->field.tqe_next;          \
} while (/*CONSTCOND*/0)

#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do {      \
    if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
        (elm)->field.tqe_next->field.tqe_prev =         \
            &(elm)->field.tqe_next;             \
    else                                \
        (head)->tqh_last = &(elm)->field.tqe_next;      \
    (listelm)->field.tqe_next = (elm);              \
    (elm)->field.tqe_prev = &(listelm)->field.tqe_next;     \
} while (/*CONSTCOND*/0)

#define TAILQ_INSERT_BEFORE(listelm, elm, field) do {           \
    (elm)->field.tqe_prev = (listelm)->field.tqe_prev;      \
    (elm)->field.tqe_next = (listelm);              \
    *(listelm)->field.tqe_prev = (elm);             \
    (listelm)->field.tqe_prev = &(elm)->field.tqe_next;     \
} while (/*CONSTCOND*/0)

#if ! DEBUG
#define TAILQ_REMOVE(head, elm, field) do {             \
    if (((elm)->field.tqe_next) != NULL)                \
        (elm)->field.tqe_next->field.tqe_prev =         \
            (elm)->field.tqe_prev;              \
    else                                \
        (head)->tqh_last = (elm)->field.tqe_prev;       \
    *(elm)->field.tqe_prev = (elm)->field.tqe_next;         \
} while (/*CONSTCOND*/0)
#else
#define TAILQ_REMOVE(head, elm, field) do {             \
    if ((TAILQ_NEXT((elm), field)) != NULL) {             \
        printf("Next != NULL\n"); \
        printf("Setting next=%p to prev=%p\n", \
            TAILQ_NEXT((elm), field)->field.tqe_prev, \
            (elm)->field.tqe_prev); \
        printf("Setting next=%s to prev=%s\n", \
            ptrshow2(TAILQ_NEXT((elm), field)->field.tqe_prev), \
            ptrshow2((elm)->field.tqe_prev)); \
        TAILQ_NEXT((elm), field)->field.tqe_prev =      \
            (elm)->field.tqe_prev;              \
    } else {                                \
        printf("Next == NULL\n"); \
        printf("Setting last=%s to prev=%s, first is at %s\n", \
            ptrshow2((head)->tqh_last), ptrshow2((elm)->field.tqe_prev),  \
            ptrshow((head)->tqh_first)); \
        (head)->tqh_last = (elm)->field.tqe_prev;       \
    } \
    printf("Setting prev=%s to next=%s\n", \
        ptrshow2((elm)->field.tqe_prev), ptrshow(TAILQ_NEXT((elm), field))); \
    *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);      \
} while (0)
#endif

#define TAILQ_FOREACH(var, head, field)                 \
    for ((var) = ((head)->tqh_first);               \
        (var);                          \
        (var) = ((var)->field.tqe_next))

#define TAILQ_FOREACH_REVERSE(var, head, headname, field)       \
    for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));    \
        (var);                          \
        (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))

#define TAILQ_CONCAT(head1, head2, field) do {              \
    if (!TAILQ_EMPTY(head2)) {                  \
        *(head1)->tqh_last = (head2)->tqh_first;        \
        (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
        (head1)->tqh_last = (head2)->tqh_last;          \
        TAILQ_INIT((head2));                    \
    }                               \
} while (/*CONSTCOND*/0)

/*
 * Tail queue access methods.
 */
#define TAILQ_EMPTY(head)       ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head)       ((head)->tqh_first)
#define TAILQ_NEXT(elm, field)      ((elm)->field.tqe_next)

#define TAILQ_LAST(head, headname) \
    (*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_PREV(elm, headname, field) \
    (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))

char *
strbuf(void)
{
    static int idx = 0;
    static char list[16][1000];

    char *buf = list[idx];
    ++idx;
    idx %= 16;

    *buf = 0;

    return buf;
}

typedef struct data {
    char file[64];
    int xid;
} data_t;

typedef struct dat {
#if GUARD
    data_t *data;
    void *ele_rsvp[ELE_RSVP_MAX];
    TAILQ_ENTRY(dat) d_link;
#else
    TAILQ_ENTRY(dat) d_link;
    data_t *data;
#endif
    int ele_xid;                /* identifier */
} dat_t;

TAILQ_HEAD(data_queue, dat);

// NOTE/BUG(?): this was original code but where was it initialized?
#if 0
struct data_queue dataq;
#else
struct data_queue dataq = TAILQ_HEAD_INITIALIZER(dataq);
#endif

#define XMAGIC(_ptr,_item,_magic) \
    ((void *) (((uintptr_t) (_ptr)->_item) + (_magic)))

#define HMAGIC(_ptr,_item,_idx) \
    XMAGIC(&_ptr[_idx],_item,0x01020304 + _idx)
#define EMAGIC(_ptr,_item,_idx) \
    XMAGIC(&_ptr[idx],_item,0x01020304 + _idx)

#define HINIT(_ptr,_item,_idx) \
    do { \
        (_ptr)->_item[_idx] = HMAGIC(_ptr,_item,_idx); \
    } while (0)

#define HCHECK(_ptr,_item,_idx) \
    do { \
        const void *exp = HMAGIC(_ptr,_item,_idx); \
        const void *act = _ptr->_item[_idx]; \
        printf("%s: HCHECK ptr=%p exp=%p act=%p idx=%d (%s)\n", \
            who,_ptr,exp,act,_idx,#_item); \
        if (exp != act) \
            err = 1; \
    } while (0)

char *
_ptrshow(char *bp,dat_t *ele)
{

    bp += sprintf(bp,"ele=%p",ele);

    do {
        if (ele == NULL) {
            bp += sprintf(bp," xid=NULL");
            break;
        }

#if 0
        if (ele->data != NULL)
            bp += sprintf(bp," xid=%d",ele->data->xid);
        else
            bp += sprintf(bp," xid=-1");
#else
        bp += sprintf(bp," xid=%d",ele->ele_xid);
#endif
    } while (0);

    return bp;
}

char *
ptrshow(dat_t *ele)
{
    char *buf = strbuf();
    char *bp = buf;

    bp += sprintf(bp,"(");
    bp = _ptrshow(bp,ele);
    bp += sprintf(bp,")");

    return buf;
}

char *
ptrshow2(dat_t **elp)
{
    char *buf = strbuf();
    char *bp = buf;

    do {
        bp += sprintf(bp,"(elp=%p",elp);

        if (*elp != NULL) {
            bp += sprintf(bp," ");
            bp = _ptrshow(bp,*elp);
        }

        bp += sprintf(bp,")");
    } while (0);

    return buf;
}

void
qshow(struct data_queue *list,const char *who)
{
    dat_t *ele;

    printf("qshow: ENTER (from %s)\n",who);

    TAILQ_FOREACH(ele, &dataq, d_link)
        printf("qshow: ELEMENT ele=%s\n",ptrshow(ele));

    printf("qshow: EXIT\n");
}

void
init_head(struct data_queue *list)
{
#if GUARD
    const char *who = "init_head";

    for (int idx = 0;  idx < HEAD_RSVP_MAX;  ++idx)
        HINIT(list,tqh_rsvp,idx);

    int err = 0;
    for (int idx = 0;  idx < HEAD_RSVP_MAX;  ++idx)
        HCHECK(list,tqh_rsvp,idx);
    if (err)
        exit(1);
#endif
}

void
check_head(struct data_queue *list,const char *who)
{
#if GUARD
    printf("%s: HCHECK tgh_first=%s tgh_last=%p\n",
        who,ptrshow(list->tqh_first),list->tqh_last);

    int err = 0;
    for (int idx = 0;  idx < HEAD_RSVP_MAX;  ++idx)
        HCHECK(list,tqh_rsvp,idx);

    if (err)
        exit(1);
#endif
}

data_t *
data_get_first(struct data_queue *q)
{
    dat_t *d;
    data_t *data = NULL;

    printf("data_get_first: ENTER\n");

    check_head(&dataq,"data_get_first/ENTER");

    d = TAILQ_FIRST(q);

    if (d) {
        printf("data_get_first: FIRST d=%s\n",ptrshow(d));
        qshow(q,"data_get_first/BEF");

        data = d->data;
        TAILQ_REMOVE(q, d, d_link);
        free(d);

        check_head(&dataq,"data_get_first/EXIT");

        printf("data_get_first: FIRST tqh_first=%s\n",ptrshow(q->tqh_first));
        qshow(q,"data_get_first/AFT");
    }

    printf("data_get_first: EXIT data=%d\n",(data != NULL) ? data->xid : -1);

    return data;
}

int
main(void)
{
    data_t *data;
    dat_t *ele;

    // initialize the guard areas in the queue head
    init_head(&dataq);

    printf("main: dataq=%p\n",&dataq);

    printf("main: TQHBEFORE tqh_first=%s tqh_last=%s\n",
        ptrshow(dataq.tqh_first),ptrshow2(dataq.tqh_last));

    for (int idx = 1;  idx <= 5;  ++idx) {
        data = calloc(1,sizeof(*data));
        data->xid = idx;

        ele = calloc(1,sizeof(*ele));
        ele->data = data;
        ele->ele_xid = idx;

        printf("main: BEFORE ele=%s\n",ptrshow(ele));

        TAILQ_INSERT_HEAD(&dataq,ele,d_link);

        printf("main: TQEAFTER tqe_next=%s tqe_prev=%s\n",
            ptrshow(ele->d_link.tqe_next),ptrshow2(ele->d_link.tqe_prev));
        printf("main: TQHAFTER tqh_first=%s tqh_last=%s\n",
            ptrshow(dataq.tqh_first),ptrshow2(dataq.tqh_last));

        check_head(&dataq,"main/insert");
    }

    while ((data = data_get_first(&dataq)) != NULL) {
        printf("loopq: DATAITEM xid=%d\n",data->xid);

        // NOTE: here is where the "real" code goes ...

        // release the data
        free(data);

        // define this to generate a use-after-free (which is caught by
        // compiling with -fsanitize=address)
#ifdef USEFREE
        data->xid += 1;
#endif
    }

    return 0;
}

这是我得到的程序输出(运行 通过压头):

init_head: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
init_head: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
init_head: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
init_head: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
main: dataq=0x4050c0
main: TQHBEFORE tqh_first=(ele=(nil) xid=NULL) tqh_last=(elp=0x4050c0)
main: BEFORE ele=(ele=0x606000000080 xid=1)
main: TQEAFTER tqe_next=(ele=(nil) xid=NULL) tqe_prev=(elp=0x4050c0 ele=0x606000000080 xid=1)
main: TQHAFTER tqh_first=(ele=0x606000000080 xid=1) tqh_last=(elp=0x6060000000a8)
main/insert: HCHECK tgh_first=(ele=0x606000000080 xid=1) tgh_last=0x6060000000a8
main/insert: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
main: BEFORE ele=(ele=0x6060000000e0 xid=2)
main: TQEAFTER tqe_next=(ele=0x606000000080 xid=1) tqe_prev=(elp=0x4050c0 ele=0x6060000000e0 xid=2)
main: TQHAFTER tqh_first=(ele=0x6060000000e0 xid=2) tqh_last=(elp=0x6060000000a8)
main/insert: HCHECK tgh_first=(ele=0x6060000000e0 xid=2) tgh_last=0x6060000000a8
main/insert: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
main: BEFORE ele=(ele=0x606000000140 xid=3)
main: TQEAFTER tqe_next=(ele=0x6060000000e0 xid=2) tqe_prev=(elp=0x4050c0 ele=0x606000000140 xid=3)
main: TQHAFTER tqh_first=(ele=0x606000000140 xid=3) tqh_last=(elp=0x6060000000a8)
main/insert: HCHECK tgh_first=(ele=0x606000000140 xid=3) tgh_last=0x6060000000a8
main/insert: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
main: BEFORE ele=(ele=0x6060000001a0 xid=4)
main: TQEAFTER tqe_next=(ele=0x606000000140 xid=3) tqe_prev=(elp=0x4050c0 ele=0x6060000001a0 xid=4)
main: TQHAFTER tqh_first=(ele=0x6060000001a0 xid=4) tqh_last=(elp=0x6060000000a8)
main/insert: HCHECK tgh_first=(ele=0x6060000001a0 xid=4) tgh_last=0x6060000000a8
main/insert: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
main: BEFORE ele=(ele=0x606000000200 xid=5)
main: TQEAFTER tqe_next=(ele=0x6060000001a0 xid=4) tqe_prev=(elp=0x4050c0 ele=0x606000000200 xid=5)
main: TQHAFTER tqh_first=(ele=0x606000000200 xid=5) tqh_last=(elp=0x6060000000a8)
main/insert: HCHECK tgh_first=(ele=0x606000000200 xid=5) tgh_last=0x6060000000a8
main/insert: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
main/insert: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=0x606000000200 xid=5) tgh_last=0x6060000000a8
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST d=(ele=0x606000000200 xid=5)
  qshow: ENTER (from data_get_first/BEF)
    qshow: ELEMENT ele=(ele=0x606000000200 xid=5)
    qshow: ELEMENT ele=(ele=0x6060000001a0 xid=4)
    qshow: ELEMENT ele=(ele=0x606000000140 xid=3)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
  Next != NULL
  Setting next=0x606000000228 to prev=0x4050c0
  Setting next=(elp=0x606000000228 ele=0x6060000001a0 xid=4) to prev=(elp=0x4050c0 ele=0x606000000200 xid=5)
  Setting prev=(elp=0x4050c0 ele=0x606000000200 xid=5) to next=(ele=0x6060000001a0 xid=4)
  data_get_first/EXIT: HCHECK tgh_first=(ele=0x6060000001a0 xid=4) tgh_last=0x6060000000a8
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST tqh_first=(ele=0x6060000001a0 xid=4)
  qshow: ENTER (from data_get_first/AFT)
    qshow: ELEMENT ele=(ele=0x6060000001a0 xid=4)
    qshow: ELEMENT ele=(ele=0x606000000140 xid=3)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
data_get_first: EXIT data=5
loopq: DATAITEM xid=5
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=0x6060000001a0 xid=4) tgh_last=0x6060000000a8
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST d=(ele=0x6060000001a0 xid=4)
  qshow: ENTER (from data_get_first/BEF)
    qshow: ELEMENT ele=(ele=0x6060000001a0 xid=4)
    qshow: ELEMENT ele=(ele=0x606000000140 xid=3)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
  Next != NULL
  Setting next=0x6060000001c8 to prev=0x4050c0
  Setting next=(elp=0x6060000001c8 ele=0x606000000140 xid=3) to prev=(elp=0x4050c0 ele=0x6060000001a0 xid=4)
  Setting prev=(elp=0x4050c0 ele=0x6060000001a0 xid=4) to next=(ele=0x606000000140 xid=3)
  data_get_first/EXIT: HCHECK tgh_first=(ele=0x606000000140 xid=3) tgh_last=0x6060000000a8
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST tqh_first=(ele=0x606000000140 xid=3)
  qshow: ENTER (from data_get_first/AFT)
    qshow: ELEMENT ele=(ele=0x606000000140 xid=3)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
data_get_first: EXIT data=4
loopq: DATAITEM xid=4
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=0x606000000140 xid=3) tgh_last=0x6060000000a8
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST d=(ele=0x606000000140 xid=3)
  qshow: ENTER (from data_get_first/BEF)
    qshow: ELEMENT ele=(ele=0x606000000140 xid=3)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
  Next != NULL
  Setting next=0x606000000168 to prev=0x4050c0
  Setting next=(elp=0x606000000168 ele=0x6060000000e0 xid=2) to prev=(elp=0x4050c0 ele=0x606000000140 xid=3)
  Setting prev=(elp=0x4050c0 ele=0x606000000140 xid=3) to next=(ele=0x6060000000e0 xid=2)
  data_get_first/EXIT: HCHECK tgh_first=(ele=0x6060000000e0 xid=2) tgh_last=0x6060000000a8
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST tqh_first=(ele=0x6060000000e0 xid=2)
  qshow: ENTER (from data_get_first/AFT)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
data_get_first: EXIT data=3
loopq: DATAITEM xid=3
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=0x6060000000e0 xid=2) tgh_last=0x6060000000a8
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST d=(ele=0x6060000000e0 xid=2)
  qshow: ENTER (from data_get_first/BEF)
    qshow: ELEMENT ele=(ele=0x6060000000e0 xid=2)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
  Next != NULL
  Setting next=0x606000000108 to prev=0x4050c0
  Setting next=(elp=0x606000000108 ele=0x606000000080 xid=1) to prev=(elp=0x4050c0 ele=0x6060000000e0 xid=2)
  Setting prev=(elp=0x4050c0 ele=0x6060000000e0 xid=2) to next=(ele=0x606000000080 xid=1)
  data_get_first/EXIT: HCHECK tgh_first=(ele=0x606000000080 xid=1) tgh_last=0x6060000000a8
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST tqh_first=(ele=0x606000000080 xid=1)
  qshow: ENTER (from data_get_first/AFT)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
data_get_first: EXIT data=2
loopq: DATAITEM xid=2
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=0x606000000080 xid=1) tgh_last=0x6060000000a8
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST d=(ele=0x606000000080 xid=1)
  qshow: ENTER (from data_get_first/BEF)
    qshow: ELEMENT ele=(ele=0x606000000080 xid=1)
  qshow: EXIT
  Next == NULL
  Setting last=(elp=0x6060000000a8) to prev=(elp=0x4050c0 ele=0x606000000080 xid=1), first is at (ele=0x606000000080 xid=1)
  Setting prev=(elp=0x4050c0 ele=0x606000000080 xid=1) to next=(ele=(nil) xid=NULL)
  data_get_first/EXIT: HCHECK tgh_first=(ele=(nil) xid=NULL) tgh_last=0x4050c0
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/EXIT: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
  data_get_first: FIRST tqh_first=(ele=(nil) xid=NULL)
  qshow: ENTER (from data_get_first/AFT)
  qshow: EXIT
data_get_first: EXIT data=1
loopq: DATAITEM xid=1
data_get_first: ENTER
  data_get_first/ENTER: HCHECK tgh_first=(ele=(nil) xid=NULL) tgh_last=0x4050c0
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x14253d4 act=0x14253d4 idx=0 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425405 act=0x1425405 idx=1 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425436 act=0x1425436 idx=2 (tqh_rsvp)
  data_get_first/ENTER: HCHECK ptr=0x4050c0 exp=0x1425467 act=0x1425467 idx=3 (tqh_rsvp)
data_get_first: EXIT data=-1

我早期的猜测...其中大部分 is/was 是错误的...唉...我一直在展示我可能有多么错误,但它确实让我走上了正确的轨道 [最终 ;-)]

啊...我认为我知道发生了什么[以及为什么],但是为了测试我的假设,获得初始化代码会有所帮助列表进入初始状态。我可以综合这个,但是,为了比较apples-to-apples,获得你的初始化代码可能会有所帮助。

提示:您最后的调试代码(和注释)提供了线索。 field arg 允许元素中的链接处于任何偏移量,但隐含的假设是它们处于偏移量 0。

列表和 element/link 结构使用不同的 names/types 但它们 (offset-wise) 是相同的。也就是说,查看 TAILQ_HEADTAILQ_ENTRY,我们在相同的偏移量处有 tqh_firsttqe_next,在相同的偏移量处有 tqh_lasttqe_prev .

因此,当 TAILQ_REMOVE 宏更改 tqe_next 时,它 必须 修改 tqh_first 以获得您看到的结果.

也就是说,宏假设两个结构等价,这在实践中有效。但是,它正在使用元素代码修改列表结构。这让我感到困惑,我敢打赌它也会让 coverity 感到困惑。

这些宏是旧的(从 1991 年开始),所以他们使用的那种“诡计”现在不会被写成新代码了。

我敢打赌,如果你 颠倒 你的 dat_t 结构的顺序,UB 会严重破坏,因为这两个结构不再等价。