这可能是因为双重释放skb_del。
理论上,在调用kfree_skb(skb_del)之前,可以通过refcount_read(&skb_del->users)检查skb_del->users的值,如果skb_del->users为0,则说明skb_del已经被释放。
实际上,kfree_skb()函数在skb_del最终发布时并没有设置skb_del->users为0(出于一些优化考虑),所以在skb_del之后会发布它将保持为 1,您将无法知道 skb_del 是否已被释放。
如果您仍然好奇这是否是双重释放问题,并且您可以对 skbuff 基础架构进行一些更改(仅用于本次调查),那么我们需要修改一些 skbuff 函数。
警告:玩这个功能很容易导致内核崩溃,所以要小心。但是这些修改是有效的(通过这种方式我发现了一个双免的skb)。请记住,这只是用于调查双重释放问题的建议,我不知道这些修改是否会长期影响您的系统。
我们将修改以下函数(基于内核 v5.9.1):
skb_unref() // from include/linux/skbuff.h
__kfree_skb() // from net/core/skbuff.c
kfree_skb() // from net/core/skbuff.c
consume_skb() // from net/core/skbuff.c
原 skb_unref()
static inline bool skb_unref(struct sk_buff *skb)
{
if (unlikely(!skb))
return false;
if (likely(refcount_read(&skb->users) == 1))
smp_rmb();
else if (likely(!refcount_dec_and_test(&skb->users)))
return false;
return true;
}
修改后的 skb_unref()
static inline bool skb_unref(struct sk_buff *skb)
{
if (unlikely(!skb))
return false;
if (likely(refcount_read(&skb->users) == 1)) {
smp_rmb();
refcount_set(&skb->users, 0);
} else if (likely(!refcount_dec_and_test(&skb->users))) {
return false;
}
return true;
}
原文__kfree_skb()
void __kfree_skb(struct sk_buff *skb)
{
skb_release_all(skb);
kfree_skbmem(skb);
}
修改了 __kfree_skb()
void __kfree_skb(struct sk_buff *skb)
{
if (!skb_unref(skb))
return;
skb_release_all(skb);
kfree_skbmem(skb);
}
原来的 kfree_skb()
void kfree_skb(struct sk_buff *skb)
{
if (!skb_unref(skb))
return;
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
修改后的 kfree_skb()
void kfree_skb(struct sk_buff *skb)
{
//if (!skb_unref(skb))
// return;
trace_kfree_skb(skb, __builtin_return_address(0));
__kfree_skb(skb);
}
原始的consume_skb()
void consume_skb(struct sk_buff *skb)
{
if (!skb_unref(skb))
return;
trace_consume_skb(skb);
__kfree_skb(skb);
}
修改后的 consume_skb()
void consume_skb(struct sk_buff *skb)
{
//if (!skb_unref(skb))
// return;
trace_consume_skb(skb);
__kfree_skb(skb);
}
祝调查顺利。
愿上帝与你同在。