skb_unref() 的行为是否符合预期?
Does skb_unref() behave as it's meant to?
前言:
如果您熟悉用于管理网络数据包的 linux 内核缓冲区,即 sk_buff
,那么您可能知道释放 skb:__kfree_skb()
根据我对 sk_buff
代码和 API 的理解,__kfree_skb()
是释放 sk_buff
的函数。它是一个内部辅助函数,理论上用户不应该调用它——用户应该调用它的包装函数之一,比如 kfree_skb()
或 consume_skb()
(或其他一些安全包装器)。
在调用 __kfree_skb()
之前,包装函数应该检查并减少 sk_buff
引用计数,如果它达到 0,包装函数可以调用 __kfree_skb()
来释放 sk_buff
。一些包装器自己完成,其他的只是调用 skb_unref()
(大约 3 年前添加到主线)。
希望你还在我身边。我说到点子上了。我最近注意到 skb_unref()
没有按照我的预期去做。直到最新版本的 linux 内核 v5.8,skb_unref()
看起来像这样:
/**
* skb_unref - decrement the skb's reference count
* @skb: buffer
*
* Returns true if we can free the skb.
*/
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;
}
问题:
- 阅读代码后,我注意到如果
refcount_read
returns 1,我们不会将 skb->users
减少到 0,但函数会 returns true
.
- 这意味着,如果
refcount_read
returns 1,skb_unref()
告诉 kfree_skb()
(或其他一些使用 skb_unref()
的包装函数)它可以释放 skb
(但它不会将 skb->users
减少到 0),并且如果实际释放 skb 的最后一个用户不费心将 skb
设置为 NULL
在调用 kfree_skb()
之后,当其他用户检查这个 skb
引用计数时,它可能会不小心再次尝试释放它!
- 我也通过阅读代码确保我没有弄错,我只是尝试调用
kfree_skb()
两次,然后一切都崩溃了...
- 我错了吗?也许我不明白如何正确使用
kfree_skb()/skb_unref()
?我只是想知道,如果 kfree_skb()/skb_unref()
不按应有的方式行事,事情到底是怎么回事。
好吧,这个是很久以前的explained in a comment(拼写错误LOL):
/*
* If users==1, we are the only owner and are can avoid redundant
* atomic change.
*/
长话短说:这只是一个微妙的优化。
如果引用计数为 1,这意味着您是该资源的唯一所有者,因此您可以安全地避免浪费时间以原子方式递减引用计数,并继续释放它。
if the last user that actually frees the skb doesn't bother to set the skb
to NULL
after calling kfree_skb()
, then when some other user will check this skb
refcount, it may accidentally try to free it again!
如果refcount是1,你是唯一的用户,还有谁会打电话给kfree_skb()
?除了你,没有人。
I just tried to call kfree_skb()
twice, and everything crashed
你还有什么期待? Linux 内核代码不是你的保姆,如果你 double-free 某些东西,你当然会导致崩溃。它可能会在死前打印一些警告,但仅此而已。基本上任何类型的分配都是如此。就像在任何其他 C 程序中一样,您必须确保只释放一次内存。
PS:kfree_skb()
的行为一直和你现在看到的一样,甚至在 v2.6 之前,唯一的区别是它的一些代码“最近”被移到了skb_unref()
(在 v4.13 中)。
前言:
如果您熟悉用于管理网络数据包的 linux 内核缓冲区,即 sk_buff
,那么您可能知道释放 skb:__kfree_skb()
根据我对 sk_buff
代码和 API 的理解,__kfree_skb()
是释放 sk_buff
的函数。它是一个内部辅助函数,理论上用户不应该调用它——用户应该调用它的包装函数之一,比如 kfree_skb()
或 consume_skb()
(或其他一些安全包装器)。
在调用 __kfree_skb()
之前,包装函数应该检查并减少 sk_buff
引用计数,如果它达到 0,包装函数可以调用 __kfree_skb()
来释放 sk_buff
。一些包装器自己完成,其他的只是调用 skb_unref()
(大约 3 年前添加到主线)。
希望你还在我身边。我说到点子上了。我最近注意到 skb_unref()
没有按照我的预期去做。直到最新版本的 linux 内核 v5.8,skb_unref()
看起来像这样:
/**
* skb_unref - decrement the skb's reference count
* @skb: buffer
*
* Returns true if we can free the skb.
*/
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;
}
问题:
- 阅读代码后,我注意到如果
refcount_read
returns 1,我们不会将skb->users
减少到 0,但函数会 returnstrue
. - 这意味着,如果
refcount_read
returns 1,skb_unref()
告诉kfree_skb()
(或其他一些使用skb_unref()
的包装函数)它可以释放skb
(但它不会将skb->users
减少到 0),并且如果实际释放 skb 的最后一个用户不费心将skb
设置为NULL
在调用kfree_skb()
之后,当其他用户检查这个skb
引用计数时,它可能会不小心再次尝试释放它! - 我也通过阅读代码确保我没有弄错,我只是尝试调用
kfree_skb()
两次,然后一切都崩溃了... - 我错了吗?也许我不明白如何正确使用
kfree_skb()/skb_unref()
?我只是想知道,如果kfree_skb()/skb_unref()
不按应有的方式行事,事情到底是怎么回事。
好吧,这个是很久以前的explained in a comment(拼写错误LOL):
/*
* If users==1, we are the only owner and are can avoid redundant
* atomic change.
*/
长话短说:这只是一个微妙的优化。
如果引用计数为 1,这意味着您是该资源的唯一所有者,因此您可以安全地避免浪费时间以原子方式递减引用计数,并继续释放它。
if the last user that actually frees the skb doesn't bother to set the
skb
toNULL
after callingkfree_skb()
, then when some other user will check thisskb
refcount, it may accidentally try to free it again!
如果refcount是1,你是唯一的用户,还有谁会打电话给kfree_skb()
?除了你,没有人。
I just tried to call
kfree_skb()
twice, and everything crashed
你还有什么期待? Linux 内核代码不是你的保姆,如果你 double-free 某些东西,你当然会导致崩溃。它可能会在死前打印一些警告,但仅此而已。基本上任何类型的分配都是如此。就像在任何其他 C 程序中一样,您必须确保只释放一次内存。
PS:kfree_skb()
的行为一直和你现在看到的一样,甚至在 v2.6 之前,唯一的区别是它的一些代码“最近”被移到了skb_unref()
(在 v4.13 中)。