执行memcpy拷贝以太网地址时触发SegFault段错误 已初始化指针仍崩溃
段错误根因与修复方案
核心触发原因
- 未初始化局部变量导致内存分配完全失效
get_payload_to_send函数中定义的int t1是栈上未赋值的局部变量,存储的是随机垃圾值。你直接用这个垃圾值计算内存分配大小:malloc(sizeof(t1) + 1020),会出现两种必然导致崩溃的情况:- 垃圾值为负数时,转成无符号的内存分配长度会变成超大值,malloc直接返回
NULL - 垃圾值过小(比如0、1)时,实际分配的内存远小于构造完整数据包需要的长度
由于代码完全没有对malloc的返回值做合法性检查,你直接把返回的空指针强转为struct ethhdr*。gdb观察到的eth->h_dest为NULL并非偶然:由于h_dest是struct ethhdr的第一个成员,当eth指针的基地址为NULL时,eth->h_dest的地址计算结果就是NULL + 0偏移,和调试现象完全吻合,直接证明malloc没有返回合法内存——对空指针做成员写入操作必然触发段错误。
- 垃圾值为负数时,转成无符号的内存分配长度会变成超大值,malloc直接返回
- 越界内存写操作破坏堆结构
代码中两处IP地址拷贝存在严重的长度越界:
其中memcpy(ip->saddr, inet_addr(p->ip_dest), sizeof(c) - 1); memcpy(ip->daddr, inet_addr(p->ip_source), sizeof(c) - 1);ip->saddr、ip->daddr都是只有4字节的32位地址字段,但你取了栈上20字节数组c的长度减1(也就是19字节)做拷贝长度,直接向只有4字节的字段后面越界写15字节数据,会踩坏堆内存的管理结构、相邻的TCP头、甚至其他动态内存块。堆结构被破坏后,后续任意内存操作都可能访问到非法地址,表现为随机位置的段错误。 - 类型与字节序转换使用错误
以太网头的h_proto字段是16位长度的网络序值,你用32位的字节序转换函数htonl(ETH_P_IP)给这个2字节的字段赋值,会额外多写2字节到相邻内存区域,也是越界来源。
针对性修复步骤
- 删除未初始化的
int t1变量,按真实数据包结构计算内存分配长度,并补充返回值检查:// 总长度 = 以太网头 + IP头 + TCP头 + 1000字节payload size_t total_len = sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr) + 1000; *pay = (char*)malloc(total_len); if (*pay == NULL) { perror("allocate packet buffer failed"); return; } memset(*pay, 0, total_len); - 修正所有越界的IP地址赋值逻辑:
inet_addr本身返回4字节的网络序IP值,不需要memcpy,直接赋值即可:ip->saddr = inet_addr(p->ip_dest); ip->daddr = inet_addr(p->ip_source); - 修正以太网类型字段的字节序转换:
把原来的eth->h_proto = htonl(ETH_P_IP);改成:eth->h_proto = htons(ETH_P_IP); - 修正IP头总长度字段的计算与字节序转换:
原来的ip->tot_len = sizeof(t1) + 1020;改成:// tot_len是IP头+负载的总长度,不含以太网头,且需要转网络序 ip->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + 1000); - 补充以太网MAC地址的正确拷贝逻辑,统一使用
ETH_ALEN(即6字节MAC长度)做拷贝长度:memcpy(eth->h_dest, p->h_source, ETH_ALEN); memcpy(eth->h_source, p->h_dest, ETH_ALEN);
额外优化提示
当前代码在持锁状态下调用get_payload_to_send,而函数内部存在sleep(2)的阻塞操作,会长时间持有互斥锁导致其他线程阻塞,建议把sleep操作移出临界区。
内容的提问来源于stack exchange,提问作者user786
相关产品推荐
相关产品推荐

