一周一更之snprintf踩坑记

背景

上周,在做并发测试的时候,发现程序总是在某一时刻发生Segmentation fault,但在之前未做并发测试时,并没有出现该问题,在认真的分析了core文件之后,不得不再一次明白自己是如此的too young to naive。

踩坑

在core文件中,有如下的分析以及结论:

  1. Segmentation fault的原因是因为查找哈希元素时访问了一块不可访问的内存导致的。
  2. 存储该哈希表的结构体中的内容正常。
  3. 将哈希表的数组内容打印出来后发现,该数组的应该是有效指针全部变成了不可访问的地址。
  4. 之前以为是分配哈希表的内存被释放,但是发现该内存指针正常
  5. 如果内容被改写,便想到了是否是出现内存越界导致哈希表中的内容被改写,根据这个结论,尝试将这些不可访问的地址强制转换char类型输出后发现,打印出了一串字符串,该现象也证明了猜测是对,在对这字符串分析后,发现是某段写字符串代码导致的。
  6. 在仔细分析了这段代码后,一切的罪魁祸首都是因为snprintf的错误使用导致的。

下面我将贴上与该问题类似的代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>



int main()
{
    int i          = 0;
    int iRet       = 0;
    int *pi        = {NULL};
    char *pBuf     = NULL;
    char acBuf[1024] = {0};


    pi = malloc(5 * sizeof(int));

    for(i = 0; i < 5; ++i)
    {
        pi[i] = i; 
    }

    pBuf = acBuf;
    do
    {
        pBuf += iRet;
        iRet = snprintf(pBuf, 32-iRet, "%s", "abcdeeeeeeeeeeee");
        if(iRet <= 0)
        {
            printf("buf:%s\n", acBuf);
            break;
        }

        printf("input:%d\n", pi[0]);

    }while(1);

    if(pi) free(pi);

    return 0;

}

可能有经验的看官一眼就瞧出了问题的所在,问题所在就是因为我错误的把snprintf的返回值效果与sprintf理解成了一致。根据介绍snprintf主要有以下的几点需要注意:

函数原型:int snprintf(char *str, size_t size, const char *format, …)
1、如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符(‘\0’);
2、如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符(‘\0’),返回值为欲写入的字符串长度
3、返回为格式化字符串的长度

需要我们特别的注意的是返回值并不是写入到缓冲区的长度,而是格式化字符串的长度,这也就导致了上述代码中的iRet的返回值永远是16,因此上述代码就导致了acBuf的数组发生了越界,最终导致pi数组被覆盖,成为了一块不可访问的内存。

结束语

其实,出现该问题都是因为对于此类函数并没有过多去理解,认知总是建立在之前的基础上,所以导致了问题的出现。实际中的代码量可能要庞大的多,因为越界导致的问题可能会让人抓狂不已。但是借助于GDB,许多问题都是可以迎刃而解的,关键在于耐心。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值