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_HEAD
和 TAILQ_ENTRY
,我们在相同的偏移量处有 tqh_first
和 tqe_next
,在相同的偏移量处有 tqh_last
和 tqe_prev
.
因此,当 TAILQ_REMOVE
宏更改 tqe_next
时,它 必须 修改 tqh_first
以获得您看到的结果.
也就是说,宏假设两个结构等价,这在实践中有效。但是,它正在使用元素代码修改列表结构。这让我感到困惑,我敢打赌它也会让 coverity
感到困惑。
这些宏是旧的(从 1991 年开始),所以他们使用的那种“诡计”现在不会被写成新代码了。
我敢打赌,如果你 颠倒 你的 dat_t
结构的顺序,UB 会严重破坏,因为这两个结构不再等价。
我正在使用 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_HEAD
和 TAILQ_ENTRY
,我们在相同的偏移量处有 tqh_first
和 tqe_next
,在相同的偏移量处有 tqh_last
和 tqe_prev
.
因此,当 TAILQ_REMOVE
宏更改 tqe_next
时,它 必须 修改 tqh_first
以获得您看到的结果.
也就是说,宏假设两个结构等价,这在实践中有效。但是,它正在使用元素代码修改列表结构。这让我感到困惑,我敢打赌它也会让 coverity
感到困惑。
这些宏是旧的(从 1991 年开始),所以他们使用的那种“诡计”现在不会被写成新代码了。
我敢打赌,如果你 颠倒 你的 dat_t
结构的顺序,UB 会严重破坏,因为这两个结构不再等价。