IPv6地址struct sin6_addr操作中memset溢出警告疑问
问题描述
操作struct sin6_addr中的IPv6地址(例如清空地址)时,编写了如下代码:
struct sockaddr a; memset(&(((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr), 0, 16); printf("SIZEOF: %lu\n", sizeof((((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr)));
编译时出现如下警告:
1.c:34:2: warning: 'memset' will always overflow; destination buffer has size 8, but size argument is 16 [-Wfortify-source] memset(&(((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr), 0, 16);
但printf输出的sizeof结果为16字节,看起来缓冲区应该足够。改用以下写法时则无警告:
struct sockaddr a; struct in6_addr aa; aa = ((struct sockaddr_in6 *)&a)->sin6_addr.s6_addr; memset(&aa, 0, 16);
请问哪里操作错误?
问题原因与解决方法
核心问题是**struct sockaddr的内存空间不足以容纳struct sockaddr_in6的完整结构**。
struct sockaddr是通用套接字地址结构体,通常大小仅为16字节(不同系统可能略有差异,但肯定小于struct sockaddr_in6)。而struct sockaddr_in6包含sin6_family、sin6_port、sin6_flowinfo、sin6_addr等多个字段,仅sin6_addr.s6_addr就占16字节,整个struct sockaddr_in6的大小远大于struct sockaddr。- 你将
struct sockaddr a强制转换为struct sockaddr_in6*后,访问sin6_addr.s6_addr时,该地址已经超出了a的内存范围。编译器的-Wfortify-source检测到了这个内存越界风险,因此抛出缓冲区溢出警告。 - 第二种写法中,你把
sin6_addr.s6_addr的值拷贝到了独立的struct in6_addr aa变量中,aa拥有完整的16字节内存空间,memset操作不会越界,所以没有警告。
正确的做法是直接使用struct sockaddr_in6类型的变量来操作IPv6地址:
struct sockaddr_in6 a; // 用sizeof代替硬编码的16,提升代码可移植性 memset(&a.sin6_addr.s6_addr, 0, sizeof(a.sin6_addr.s6_addr));
内容的提问来源于stack exchange,提问作者Mikhail Zakharov
相关产品推荐
相关产品推荐

