程序编写安全代码——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 ...
随机推荐
- Windows同步阿里云时间
Ctrl+R打开cmd命令框 输入:gpedit.msc 计算机配置”—“管理模版”—“系统”—“Windows 时间服务”—“时间提供程序”—“配置 Windows NTP 客户端 双击打开配置 W ...
- 九度oj 题目1029:魔咒词典
题目描述: 哈利波特在魔法学校的必修课之一就是学习魔咒.据说魔法世界有100000种不同的魔咒,哈利很难全部记住,但是为了对抗强敌,他必须在危急时刻能够调用任何一个需要的魔咒,所以他需要你的帮 ...
- 【Luogu】P3391文艺平衡树(Splay)
题目链接 ddosvoid和自为风月马前卒教了我这道题 他们好强啊 如果我们要反转区间[l,r] 我们首先把l的前驱旋转到根节点 再把r的后继旋转到根节点的右儿子 那么此时根节点的右儿子的左儿子所代表 ...
- 【Luogu】P1330封锁阳光大学(bfs染色)
题目链接 这题恶心死我了. bfs染色,统计每个联通块两色的个数,ans加它们的最小值. #include<cstdio> #include<cctype> #include& ...
- Fruit Ninja
Fruit Ninja 时间限制:C/C++ 5秒,其他语言10秒 空间限制:C/C++ 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 Fruit Ni ...
- uva 10870 递推关系矩阵快速幂模
Recurrences Input: standard input Output: standard output Consider recurrent functions of the follow ...
- iOS常用三方库收集
除非Pod可以直接加载到工程中的外,收集一下 https://github.com/kejinlu/KKGestureLockView 好用的手势解锁
- Day 12 shell语法及程序若干
1. 现归纳一下shell中的运算符: +:对两个变量做加法. -:对两个变量做减法. *:对两个变量做乘法. /:对两个变量做除法. **:对两个变量做幂运算. ...
- luogu P1149 火柴棒等式
题目描述 给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?等式中的A.B.C是用火柴棍拼出的整数(若该数非零,则最高位不能是0).用火柴棍拼数字0-9的拼法如图所示: 注意: 加号与等号各自 ...
- java集合系列之LinkedList源码分析
java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...