0


lwIP更新记08:TCP 回调函数中调用 tcp_abort 终于安全了

从 lwIP-1.4.0 开始,tcp 回调函数中调用

tcp_abort

函数终于安全了。
在此之前,如果从 tcp 回调函数中调用

tcp_abort

,则会访问未分配的内存。

应用程序关闭连接,正常情况下是调用

tcp_close

函数,经过 4 次握手安全的断开连接。但 lwIP 还支持另外一种关闭连接的 API 函数:

tcp_abort

。这个函数用于中止连接,即发生了异常情况,强制关闭连接。

但是在 lwIP-1.4.0 之前,应用层使用

tcp_abort

可能会有问题。

2009 年 10 月 30 日,

Simon Goldschmidt

报告了这个 BUG。他在

httpd

中发现了这个 BUG,

httpd

是 lwIP 内置的一个网页服务器程序,使用

raw API

编写。在

httpd

recv

回调函数中,当检测到状态错误,会调用

tcp_abort

并返回

ERR_ABRT

错误码。该错误码表示 **应用程序调用了

tcp_abort

函数**。

tcp_abort

函数首先会将 pcb 控制块从有效链表中删除,然后释放控制块中未应答段、无序段、未发送段的内存(如果有的话),再发送一个

RST

帧告诉远端连接我要强制断开了,最后释放 pcb 控制块内存。

所以调用完

tcp_abort

后,由于 pcb 控制块内存被释放,不能再使用了,但偏偏 lwIP 设计有漏洞,比如在调用完

recv

回调函数后,还使用了已经释放掉的 pcb 控制块,参见以下代码(代码有删减) :

if(recv_data !=NULL){/* Notify application that data has been received. */TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);// <--- 这里调用 recv 回调函数,有可能使用 tcp_abort}

tcp_input_pcb =NULL;/* Try to send something out. */tcp_output(pcb);// <--- 这里没有判断 ERR_ABRT,pcb 指向的内容可能已释放

随后,在 2010 年 1 月 28 日,

Simon Goldschmidt

修复了这个问题,修复后的代码在调用完 tcp 回调函数后,判断一下错误码是否是

ERR_ABRT

,如果是,则表示用户在回调函数中调用了

tcp_abort

函数,释放了 pcb 控制块内存,lwIP 内核则不会再使用这个 tcp 控制块(代码有删减):

if(recv_data !=NULL){/* Notify application that data has been received. */TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);if(err == ERR_ABRT){goto aborted;// <--- 这里跳走了}}
tcp_input_pcb =NULL;/* Try to send something out. */tcp_output(pcb);

但事情还没结束。

和其它 tcp 回调函数相比,

recv

回调函数具有特殊性。内核调用

recv

回调函数时,会向其传递一个

pbuf

指针,指向网卡接收到的数据。

recv

回调函数无论是正确处理这些数据(返回

ERR_OK

),还是因为某些原因中止连接(返回

ERR_ABRT

),**都需要在这个回调函数中释放

pbuf

内存**,否则就会存在内存泄漏问题。

但是,在 lwIP-2.1.0 之前,注册

recv

回调函数的说明文档存在错误。它错误的表述为返回

ERR_OK

时一定要释放

pbuf

,其它情况一定不要释放

pbuf

— 没有提及返回

ERR_ABRT

的情况。文档如下所示:

Sets the callback function that will be called when new data arrives. The callback function will be passed a NULL pbuf to indicate that the remote host has closed the connection.
If there are no errors and the callback function is to return ERR_OK, then it must free the pbuf. Otherwise, it must not free the pbuf so that lwIP core code can store it.

注意黑色加粗字体,翻译过来是:

如果回调函数没有错误并且返回ERR_OK,那么它必须释放 pbuf。否则,它不能释放 pbuf,以便 lwIP 内核存储这包数据(等到机会合适会再次提交给应用层处理)。

API 文档说明是开发人员最重要的参考依据,它的描述必须准确无误。所以这是一个 BUG,是可能引起用户内存泄漏的严重 BUG。

2017 年 9 月 25 日

Ambroz Bizjak

提交了这个 BUG,2017 年 10 月 16,开发人员

Dirk Ziegelmeier

修正了这个错误,修正后的文档描述为:

Sets the callback function that will be called when new data arrives. The callback function will be passed a NULL pbuf to indicate that the remote host has closed the connection.
If the callback function returns ERR_OK or ERR_ABRT it must have freed the pbuf, otherwise it must not have freed it.

黑色加粗字体翻译过来是:

如果回调函数返回

ERR_OK

或者

ERR_ABRT

,那么它必须释放 pbuf,否则,它必须不能释放 pbuf。

这也是

recv

回调函数编程的一个注意事项。

然而,更好的方法是应用层永远不要使用

tcp_abort

函数,使用

tcp_close

就足够了。

tcp_abort

函数存在的意义是提供给 lwIP 内核使用的,但是最初的设计者不知道出于什么考虑,将它开放成一个外部 API。在漫长的使用过程中,肯定有人在应用层使用了这个函数,所以为了保持兼容性,只能以补丁的方式堵上这个漏洞。

这也是为什么接口函数必须花时间仔细考虑的原因,一旦有人使用,就再也不容易更改了。模块代码、协议、解决方案,甚至是硬件,都符合这个道理。因此,在最开始的时候花大量时间是值得的,可以看看我的这篇博文。


读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
千金难买知识,但可以买好多奶粉

标签: tcp/ip 网络 lwip

本文转载自: https://blog.csdn.net/zhzht19861011/article/details/130874532
版权归原作者 研究是为了理解 所有, 如有侵权,请联系我们删除。

“lwIP更新记08:TCP 回调函数中调用 tcp_abort 终于安全了”的评论:

还没有评论