调用getline后使用其len参数作遍历边界导致后续调用崩溃问题排查
问题触发原因分析
核心错误本质
你遇到的sysmalloc断言失败是堆内存越界写入破坏malloc元数据的典型表现,malloc分配堆块时会在块前后存储管理用的元数据(比如块大小、是否被使用的标记位),越界写会把这些元数据篡改成非法值,当malloc下一次申请内存扫描堆结构时就会触发断言校验失败。
具体根因拆解
- 首先要明确
getline接口的参数定义:getline(&buff, &len, f)中的len存储的是buff指向的缓冲区的总分配容量,不是当前读取到的字符串的实际有效长度。getline会自动扩容缓冲区,通常缓冲区容量会比实际读到的行内容长度大,只有getline的返回值才是本次读取到的实际字符数(包含末尾换行符,不包含字符串结束符\0)。 - 原有代码的越界逻辑:
你用len作为循环拷贝的上限,相当于把buff里整个缓冲区的内容(包括有效字符串之后的垃圾数据、未使用的缓冲区空间)全部拷贝到p->first数组中。已知p->first分配仅能容纳正常的姓氏字符串,不足以容纳整个缓冲区长度的内容,就会发生越界写入。 - 第二次迭代报错的原因:
第一次循环的越界写破坏了malloc在堆上存储的下一个空闲块的元数据,第一次申请内存时不会触发全局校验,等到第二次循环调用malloc申请person结构体内存时,malloc扫描堆结构校验元数据合法性,读取到被篡改的非法值就触发了sysmalloc的断言失败。 - 改
strlen(buff)后问题消失的原因:strlen(buff)会返回buff中有效字符串的长度(到\0截止,不包含结束符),拷贝长度刚好是实际读到的姓氏内容长度,不会超过p->first的分配容量,不会发生越界写入,堆元数据不会被破坏,错误自然消失。
优化建议
可以直接用getline的返回值控制拷贝长度,避免额外调用strlen,性能更好,同时建议用strncpy类的安全接口做字符串拷贝,主动截断防止溢出:
ssize_t read_len = getline(&buff, &len, f); if (read_len > 1) { person *p = malloc(sizeof(person)); // 减去末尾的换行符长度 strncpy(p->first, buff, read_len - 1); p->first[read_len - 1] = '\0'; }
内容的提问来源于stack exchange,提问作者limbstoinfty311
相关产品推荐
相关产品推荐

