最近处理一起客户问题,客户反馈使用我司 SDK 后偶现异常,只有重启计算机才能正常。客户的软件产品质量受到最终用户质疑,不得已只能提供排不使用我司 SDK 的“稳定版”给用户使用。
起初排查我司的是否存在 bug ,经过一番排查最后定位到客户代码存在 问题,造成偶现 bug 的代码如下:

// 代码来自网上
// 将十六进制的字符串转换成二进制数据
void HexToUnsignedChar(char *intput, unsigned int inputLongth, unsigned char *output)
{char * inputTemp = intput;unsigned char *cc = output;int n;char temp[2];unsigned int j = 0;for (int i = 0; i < inputLongth; i++){temp[0] = *inputTemp;temp[1] = *(inputTemp + 1);inputTemp = inputTemp + sizeof(char) * 2;sscanf_s(temp, "%x", &j);*cc = j;cc++;}
}

惊现 BUG

Debug 一切正常

默认内存填充 CC,隐藏 BUG,无法发觉问题。

Release 偶现 BUG

随机栈数据引发的 BUG,Release 没有编译器默认填充的 CC 数据,程序运行,函数栈数据是随机的。

代码分析

  1. 函数栈数据一般也会使用字节对齐机制,局部变量 temp 分配两个字节内存,根据字节对齐的规则,程序运行的内存中后续至少有2个字节的随机数据。
  2. sscanf_s 会根据格式 %xtemp[0] 指针位置直到字符串结尾,最长转换8个字符(4字节),因为 temp 数组并未设定截止符 “\0” ,当随机数据满足 16 进制的规则(09,af, A~F)时,实际转换的字符长度大于2。
  3. *cc = j 时,变量 ccunsigned char 类型(1字节),将 j(int) 的数据赋值给 cc(unsigned char) 时会强制类型转换,截取 j(int) 的最低位字节数据赋值给 cc。

假设一种场景:

输入转换字符串数据:FD,对应的二进制数据 0x46, 0x44
期望转换后的结果:0xFD
随机栈数据0x33, 0x11 ,此时 temp 内存指向数据为 0x46, 0x44, 0x33, 0x11 ,手动构造复现问题情境,修改 temp 变量后内存如下图所示:
[外链图片转存失败(img-Gz2FLygq-1564929965656)(index_files/109d8905-dff0-4a2c-8211-28b2ec241ab3.png)]
实际转换结果:0xD3

代码执行逻辑如下:

1.temp[2] = {0x46, 0x44}   // 随机栈数据 0x33, 0x11
2.sscanf_s(temp, "%x", &j); 执行后 j = 0x0FD3   // 因为 0x11 的 ASCII 不符合转二进制的规则([0,9],[a,z],[A,Z]),会被当做终止符。
3.*cc = 0xD3

场景复现

复现方法,编写示例程序,编译成 Release 版本,在代码中增加调试调试信息,检查 temp 变量后是否存在随机栈数据,如果存在,则输出调试信息,反复执行多次。

测试代码:

#include <stdio.h>
#include <string.h>//{x012,0x34} -> "1234"
int bytes_to_hexstr( const void* data,  int count,  char* hex)
{int i = 0;for( i; i < count; i++)sprintf(hex + 2*i, "%02X", ((unsigned char*)data)[i]);return 0;
}void HexToUnsignedChar(char *intput, unsigned int inputLongth, unsigned char *output)
{char * inputTemp = intput;unsigned char *cc = output;int n;char temp[2];unsigned int j = 0;for (int i = 0; i < inputLongth; i++){temp[0] = *inputTemp;temp[1] = *(inputTemp + 1);// 测试代码:检查内存中 temp 变量后的随机栈数据if ((temp[2] > '0' && temp[2] < '9') || (temp[2] > 'a' && temp[2] < 'f') || (temp[2] > 'A' && temp[2] < 'F')){printf("%d temp = %s\n", i + 1, temp);//getchar();}inputTemp = inputTemp + sizeof(char) * 2;sscanf_s(temp, "%x", &j);*cc = j;cc++;}
}int main()
{// 测试unsigned char outbuf[256] = {0};char code[] = "FD66C759F7F578985DA3AF8057C56E32";HexToUnsignedChar(code, strlen(code) / 2, outbuf);// 输出转换后的结果char new_buf[256] = {0};bytes_to_hexstr(outbuf, strlen(code) / 2, new_buf);printf("new_buf = %s\n", new_buf);getchar();return 0;
}

执行多次,得到一次异常结果:

1 temp = FD3
2 temp = 663
3 temp = C73
4 temp = 593
5 temp = F73
6 temp = F53
7 temp = 783
8 temp = 983
9 temp = 5D3
10 temp = A33
11 temp = AF3
12 temp = 803
13 temp = 573
14 temp = C53
15 temp = 6E3
16 temp = 323
new_buf = D363739373538383D333F3037353E323

从执行结果可以看出,问题复现时,内存中 temp 变量后存在随机数据 0x33 和无效随机值,造成转换结果与预期不一致,并且会对整个字符串的转换过程产生持续性的影响。

解决方案

  1. temp 指定结束符,例如:temp[3] = {0}
  2. sscanf_s 转换时指定长度(2),例如:sscanf_s(temp, “%02x”, &j);

问题反思

  1. 随机栈数据时非常难以发现的一种bug,对程序的健壮性产生极大的影响,随机偶现的特性决定问题一旦复现,会造成程序一直无法正常运行的情况,直到重启,产生的随机数据不会对程序功能产生影响。
  2. C 语言代码简单,但函数使用过程中容易忽略细节,尤其是内存操作:淹栈、溢出、内存泄露都是非常常见的问题,写代码时需要重点留意每一行代码的执行逻辑和预期结果的判断,否则会导致代码失控。

C 随机栈数据引发的bug相关推荐

  1. hackintool怎么重建缓存_mysql表数据装满了怎么办?自增id引发的bug。

    国庆节的一个大清早,我就被微信群吵醒了,说是spark实时程序有问题,数据没出来,让我看看.我看了下邮箱,确实有报警信息.我特么,没办法,起床. 1 主键自增id设置不合理引发的bug 首先,我检查了 ...

  2. 谷歌程序员犯低级错误?少打一个字符引发重大 Bug,致大量 Chromebook 无法解锁...

    整理 | 郑丽媛 出品 | CSDN(ID:CSDNnews) 初学编程的程序员难免会犯一些低级错误,这不难理解. 可当这种低级错误出现在谷歌经过三个开发者版本进而推出的 Chrome OS 正式版中 ...

  3. 一个由服务器时区引发的Bug

      太阳照常升起,在每个需要挤公交车上班的日子里,即使窗外早已大雨如注.想来只有在周末,太阳会陪着我一起起床,所谓睡觉睡到自然醒,在雨天里保持晴天的心情,相当大的程度上,是因为今天不必上班.因此,一周 ...

  4. Web 开发人员必备的随机 JSON 数据生成工具

    在 Web 开发中,经常会需要一些测试数据来测试接口或者功能时候正确.JSON Generator 就是这样一款生成随机 JSON 数据的在线工具,Web 开发人员必备,记得收藏和分享啊. 您可能感兴 ...

  5. vb6 数据自动生成excel文件_Excel随机生成数据

    工作中有时候需要大量数据,而手头没有现成的数据怎么办?,很多小伙伴苦思冥想编写数据,意义不大还累个半死,今天推出两种方法随机生成数据小妙招. 1.用rand函数生成A与B之间的随机数字(A≤随机数≤B ...

  6. mysql 随机选择数据_从MySQL随机选取数据

    从MySQL随机选取数据最简单的办法就是使用"ORDER BY RAND()"; 方案一: SELECT * FROM `table` ORDER BY RAND() LIMIT  ...

  7. lamda表达式 随机取数据的方法

    随机取数据,关键要用到Guid.NewGuid()才可以的,Take(10)表示随机取10条记录 var _icpics = DB.I_FirstRecommend.GetAllList().Wher ...

  8. mysql 随机查询数据

    2019独角兽企业重金招聘Python工程师标准>>> 在mysql中查询5条不重复的数据,使用以下: 1 SELECT * FROM `table` ORDER BY RAND() ...

  9. 从MySQL随机选取数据

    --从MySQL随机选取数据 -------------------------2014/06/23 从MySQL随机选取数据最简单的办法就是使用"ORDER BY RAND()" ...

最新文章

  1. 笔记本电脑处理器_苹果高管:我们都没有想到M1的性能有那么强|英特尔|处理器|iphone|ipad|笔记本电脑...
  2. Stucts应用引起的OutOfMemoryError
  3. 修改项目名称后,部署到tomcat问题
  4. 数据探查与可视化平台
  5. Python 图像处理篇-利用opencv库和numpy库读取包含中文路径下的本地图片实例演示
  6. Spring-data-redis入门
  7. LOJ #516. 「LibreOJ β Round #2」DP 一般看规律
  8. php 获得手机位置坐标,获取安卓手机的坐标位置
  9. History of program(1950-2020)
  10. 聊聊数仓中TPCD-DSTPC-H与查询性能的那些事儿
  11. httplistener java_可以使用异常HttpListener吗?
  12. 学习总在继续......
  13. 查看 php yii脚本位置,Yii框架分析(一)入口脚本index.php的启动过程剖析
  14. R语言在C#使用DCom中遇到的若干问题
  15. Spring Cloud Alibaba Seata处理分布式事务及案例实战
  16. 我应该通过软件或硬件调整音量以获得最佳声音吗?
  17. Linux 实现人脸识别
  18. Java 正则表达式的用法与实例
  19. 漫画阅读器:DreamView for Mac
  20. Codeforces Edu:双指针 » Step 3 » Practice:A. Looped Playlist

热门文章

  1. Ansible的管理变量、机密和事实
  2. 公务员备考(六) 行测 言语理解
  3. Java 获得当天的最后一秒的时间
  4. 【已解决】Edge地址搜索栏回车后响应慢
  5. java的isEmpty与isBlank的用法
  6. Linux设备驱动程序——PCI相关知识的学习
  7. 文件的名字带有特殊符号下载报错问题
  8. 小米9SE 更新MIUI10.3.3.0稳定版本,修复安卓动态壁纸黑屏Bug
  9. 【Python技能树共建】字符串方法
  10. 冬天来了,这份脚手架冬季施工安全宝典请收好!