程序编写安全代码——sendto和recvfrom的大坑
近日帮一个兄弟查代码问题,再处理完一系列问题以后,发现程序某些时候工作还是不正常,甚至会崩溃。因为环境所限,不能使用gdb,所以我只能review他的代码。最终发现原来是sendto和recvfrom挖的坑。
让我们看一下sendto和recvfrom的原型:
#include <sys/types.h>
#include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
众所周知,对于TCP/IP的socket来说,也就是AF_INET的socket,其地址类型应该为struct sockaddr_in,在调用sendto和recvfrom不得不进行强制类型转换——万恶的强制类型转换!!!代码要写成这样:
sendto(sock, buf, len, (const struct sockaddr *)&dest_addr, sizeof(dest_addr));
而这次的bug就是由于这个强制类型转换造成的。
原来的代码大致如下:
int send_func(int sock, const char *buf, int len)
{
struct sockaddr_in dst;
/* 省略初始化dst */
/* 省略其它业务逻辑 */
return sendto(sock, buf, len, 0, (const struct sockaddr*)&dst, sizeof(struct sockaddr_in));
}
对代码进行了重构,目的地址变为了参数,结果代码变成了下面这个样子:
int send_func(int sock, const char *buf, int len, struct sockaddr_in *dst)
{
/* 省略其它业务逻辑 */
return sendto(sock, buf, len, 0, (const struct sockaddr*)&dst, sizeof(struct sockaddr_in));
}
错误就在于dst从原来的struct sockaddr_in类型,变为了struct sockaddr_in指针,但是sendto的语句却忘了做相应的改动,这样sendto写入到了错误的地址&dst,导致内存错误的发生。这个bug在当前的示例中,好像很容易找到,但是在真正的工程中,在大量的代码中找到这个问题就不是那么容易的事情了。
那么我们如何避免这样类似的bug呢?从这个教训中,我们要得到经验——强制类型转换是万恶之源。为什么要强制转换呢?肯定是因为你的代码不满足被调用者的要求,这已经意味着危险了。另外,由于使用了强制类型转换,这意味着gcc编译器无法发现类型不匹配的错误,这也是上面例子中,问题没有在编译阶段发现的原因。
这样的话,我们就要问一下,为什么sendto和recvfrom要这样设计呢?这是因为sendto和recvfrom面对的所有类型的socket。因此地址不仅仅是AF_INET的地址类型struct sockaddr_in,比如对于Unix域socket,地址类型则是struct sockaddr_un。面对这么多不同地址类型,C本身又不支持函数重载,因此只能定义一个“通用”的结构。
最后如何来避免这种类似问题呢?我们无法改变sendto的用法,但是可以通过封装来解决这一问题——封装本来就是用于隔离变化,隔离问题的手段。
static inline int inet_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr_in *dest_addr)
{
return sendto(sockfd, buf, len, flags, (const struct sockaddr*)dest_addr, sizeof(*dest_addr));
} static inline int un_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr_un *dest_addr)
{
return sendto(sockfd, buf, len, flags, (const struct sockaddr*)dest_addr, sizeof(*dest_addr));
}
通过定义inline函数,来封装强制类型转换,对于每一种具体的socket,都调用封装的inline函数,虽然看上去多写了几行代码,但却是一劳永逸。
将问题或者潜在的问题消除,才能真正提高工作效率和产品质量。
程序编写安全代码——sendto和recvfrom的大坑的更多相关文章
- 初识Java程序,编写简单代码?
Dear All: 初识Java程序,编写简单代码? 首先小编在这里说下我们今天编写Java程序使用的是 eclipse 开发工具! 1.下载eclipse 官网地址:http://www.eclip ...
- [python]通过微信公众号“Python程序员”,编写python代码
今天发现微信公众号中,居然可以编写python代码,很是惊喜,觉得蛮有趣的. 步骤如下: 1.关注微信公众号“Python程序员” 2.关注成功后,点击右下角的“潘多拉”->"Pyth ...
- Linux下的 sniff-andthen-spoof程序编写
Linux下的 sniff-andthen-spoof程序编写 一.任务描述 在本任务中,您将结合嗅探和欺骗技术来实现以下嗅探然后欺骗程序.你需要两台机器在同一个局域网.从机器A ping IP_X, ...
- [转]通过Visual Studio为Linux编写C++代码
Build 2016大会上Microsoft首次公布的Visual Studio 2015扩展提供了在VS2015中编写C++代码,随后通过Linux/UNIX计算机进行编译和执行的能力.这种想法非常 ...
- 基于CkEditor实现.net在线开发之路(2)编写C#代码,怎么调用它。
上一章简约的介绍了CkEditor编辑器,可以编辑js逻辑代码,css,html,C#代码,这章我根据实际例子,讲解怎么编写C#代码和怎么调用它. 大家都还记得刚刚接触程序编时的hello Word吧 ...
- Linux进程间通信(九):数据报套接字 socket()、bind()、sendto()、recvfrom()、close()
前一篇文章,Linux进程间通信——使用流套接字介绍了一些有关socket(套接字)的一些基本内容,并讲解了流套接字的使用,这篇文章将会给大家讲讲,数据报套接字的使用. 一.简单回顾——什么是数据报套 ...
- (十四)UDP协议的两个主要方法sendto和recvfrom详解
在网络编程中,UDP运用非常广泛.很多网络协议是基于UDP来实现的,如SNMP等.大家常常用到的局域网文件传输软件飞鸽传书也是基于UDP实现的. 本篇文章跟大家分享linux下UDP的使用和实现,主要 ...
- 解决VS2012编写JQuery代码不能智能提示的问题(其他js库的代码提示设置估计类似)
VS默认设置下编写jQuery代码是这样的: 解决办法: 1.在项目的"管理NuGet程序包"中安装JQuery: 2.打开:工具 -> 选项 -> 文本编辑器 -&g ...
- UNIX网络编程-send、recv、sendto、recvfrom详解
send.recv和sendto.recvfrom,一般情况下,send.recv在TCP协议下使用,sendto.recvfrom在UDP协议下使用,也可以在TCP协议下使用,不过用的很少. 1.s ...
随机推荐
- Linux 指令的快捷键
- 图论trainning-part-1 G. Stockbroker Grapevine
G. Stockbroker Grapevine Time Limit: 1000ms Memory Limit: 10000KB 64-bit integer IO format: %lld ...
- HDU-3743 Minimum Sum,划分树模板
Minimum Sum 被这个题坑了一下午,原来只需找一个最中间的数即可,我以为是平均数. 题意:找一个数使得这个数和区间内所有数的差的绝对值最小.输出最小值. 开始用线段树来了一发果断T了,然后各种 ...
- Percona XtraBackup 2.4新特性之恢复单个表数据
参考文档:https://www.percona.com/doc/percona-xtrabackup/2.4/xtrabackup_bin/restoring_individual_tables.h ...
- P1373 小a和uim之大逃离 (动态规划)
题目背景 小a和uim来到雨林中探险.突然一阵北风吹来,一片乌云从北部天边急涌过来,还伴着一道道闪电,一阵阵雷声.刹那间,狂风大作,乌云布满了天空,紧接着豆大的雨点从天空中打落下来,只见前方出现了一个 ...
- java面试题之什么是ThreadLocal?底层如何实现的?
ThreadLocal是一个解决线程并发问题的一个类,用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术.但是当我们不想使用同步的时 ...
- zoj 2562 反素数
题目大意:求n范围内最大的反素数(反素数定义:f(x)表示x的因子数,f(x)>f(x1) (0<x1<x)) x用质因数形式为:x=a1^p1*a2^p2......an^pn(a ...
- Codeforces Round #288 (Div. 2) E. Arthur and Brackets [dp 贪心]
E. Arthur and Brackets time limit per test 2 seconds memory limit per test 128 megabytes input stand ...
- [转发]Android 系统稳定性 - ANR(二)
文章都为原创,转载请注明出处,未经允许而盗用者追究法律责任. 很久之前写的了,留着有点浪费,共享之.编写者:李文栋P.S. OpenOffice粘贴过来后格式有些混乱. http://rayleeya ...
- 王垠 :写给支持和反对《完全用Linux工作》的人们 ( 2004/8/26)
在一阵阵唾骂和欢呼声中,<完全用linux工作>被转载到了天涯海角.很多人支持我,也有很多人唾骂我.我不知道它是怎样流传到那么多地方,其实 我不知道它都被转到哪里去了…… 我知道现在这篇文 ...