bug诞生记——不定长参数隐藏的类型问题
这个bug的诞生源于项目中使用了一个开源C库。由于对该C库API不熟悉,一个不起眼的错误调用,导致一系列诡异的问题。最终经过调试,我们发现发生了内存覆盖问题。为了直达问题根节,我将问题代码简化如下(转载请指明出于breaksoftware的csdn博客)
#include <iostream>
#include <stdarg.h>enum type {PARAM,RESULT
};void set_zero(type t, ...) {va_list arg;va_start(arg, t);if (PARAM == t) {long* param_longp = va_arg(arg, long *);*param_longp = 0;} else {int* param_intp = va_arg(arg, int *);*param_intp = 0;}va_end(arg);
}int main() {int x = 1;int y = 2;set_zero(PARAM, &y);std::cout << "x = " << x << "; y = " << y << std::endl;return 0;
}
如果只是简单看一下main函数,可以认为输出是
x = 1; y = 0
然而实际输出是
x = 0; y = 0
是不是很诡异?我们在main函数中只是把y的值从2修改成0,根本没有“动”过x变量。但是最终x的值变成了0。
由于示例足够简单,我们可以通过阅读源码来定位问题。第26行传递的参数y是4个字节的int类型。而在第13行,发现参数被当成8个字节的long类型设置为0,这样就覆盖了y空间之后的4个字节。而x变量正好在内存上位于y变量之后,这样x的值也会被改成0。
现实中,我们的场景比较复杂,最终我们通过GDB来确定该问题。其过程大致如下
Reading symbols from ./test...done.
(gdb) b 26
Breakpoint 1 at 0xb0a: file main.cpp, line 26.
(gdb) r
Starting program: /home/fangliang/projects/test_cover/test Breakpoint 1, main () at main.cpp:26
26 set_zero(PARAM, &y);
(gdb) p &x
$1 = (int *) 0x7fffffffe434
(gdb) p x
$2 = 1
(gdb) p &y
$3 = (int *) 0x7fffffffe430
(gdb) p y
$4 = 2
(gdb) x/2x &y
0x7fffffffe430: 0x00000002 0x00000001
(gdb) awatch x
Hardware access (read/write) watchpoint 2: x
(gdb) c
Continuing.Hardware access (read/write) watchpoint 2: xOld value = 1
New value = 0
set_zero (t=PARAM) at main.cpp:21
21 }
(gdb) disas
……0x0000555555554a64 <+234>: mov -0xd8(%rbp),%rax0x0000555555554a6b <+241>: movq $0x0,(%rax)
=> 0x0000555555554a72 <+248>: jmp 0x555555554acb <set_zero(type, ...)+337>
……0x0000555555554acb <+337>: nop0x0000555555554acc <+338>: mov -0xb8(%rbp),%rax
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) i r rax
rax 0x7fffffffe430 140737488348208
(gdb) x/2x 0x7fffffffe430
0x7fffffffe430: 0x00000000 0x00000000
第2行在代码第26行下了断点,为了让我们可以在main函数中查看x、y变量的地址和值。
第10,14和18行可以看出x和y变量的内存空间是连续的。
第19行我们给“莫名”被修改的变量x下了内存读写断点。执行continue后,由于x的值被从1改成0,从而触发了断点。
第30行,我们查看当前代码处的汇编指令。
第33行,是触发内存断点,即x的值被修改的位置。movq是给8个字节赋值,于是我们只要验证rax地址是否就是y变量的地址。
第41行验证了rax地址就是y变量地址,从而可以证明就是movq $0x0,(%rax)导致x变量值被改变。
第43行,我们查看此时x和y的内存空间的值,它们已经都是0了。
如果我们把set_zero方法改成针对y变量的函数
void set_param(long* param_longp) {*param_longp = 0;
}
这样如果我们给其传递int型变量,编译器就会报错
main.cpp: In function ‘int main()’:
main.cpp:30:14: error: cannot convert ‘int*’ to ‘long int*’ for argument ‘1’ to ‘void set_param(long int*)’set_param(&y);
而使用可变长参数则正好掩盖了该问题。
bug诞生记——不定长参数隐藏的类型问题相关推荐
- bug诞生记——临时变量、栈变量导致的双杀
这是<bug诞生记>的第一篇文章.本来想起个文艺点的名字,比如<Satan(撒旦)来了>,但是最后还是想让这系列的重心放在"bug的产生过程"和" ...
- python3 函数 不定长参数 不定参
第一种不定长参数*args *args 称为不定长参数,只能放在形参的最后位置,返回的是一个元组 def num(a,b,*args):print(a)print(b)print(args)num(1 ...
- 不定长参数的装包与拆包
#转载请联系 def task(a,b,c,*args,**kwargs):print(a)print(b)print(c)print(args)print(kwargs)task(1,2,3,4,5 ...
- python不定长参数怎么相加_python函数不定长参数使用方法解析
这篇文章主要介绍了python函数不定长参数使用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 pathon中的函数可以使用不定长参数,可以 ...
- python不定长参数详解
不定长参数 如果想要一个函数能不固定接收任意多个参数,可以使用不定长参数. 1.不定长参数两种基本形式: python自定义函数中有两种不定长参数,第一种是*XXX,在传入额外的参数时可以不用指明参数 ...
- python笔记之函数参数(缺省参数,命名参数,不定长参数)
缺省参数 函数中定义带有初始值的形参 参数调用时,缺省参数可传,可不传 缺省参数一定在参数列表的最后面 缺省参数的数量没有限制 def x_y_sum(x,y=20): #缺省参数要在参数列表的最后p ...
- java 不定参数方法_java中不定长参数的使用方法
java中不定长参数的使用方法 不定长参数方法的语法如下:返回值 方法名(参数类型...参数名称) 在参数列表中使用"..."形式定义不定长参数,其实这个不定长参数a就是一个数组, ...
- C语言 函数不定长参数 ##__VA_ARGS__经典案例 - C语言零基础入门教程
目录 一.##__VA_ARGS__简介 二.##__VA_ARGS__经典案例 三.猜你喜欢 零基础 C/C++ 学习路线推荐 : C/C++ 学习目录 >> C 语言基础入门 一.## ...
- C语言 函数不定长参数 - C语言零基础入门教程
目录 一.前言 二.函数不定长参数简介 1.va_start 2.va_arg 3.va_end 三.自定义不定长参数的函数 1.va_start/va_arg/va_end 案例一 2.va_sta ...
最新文章
- ASP.NET 父页面取子页面的值
- 鸿蒙开发-从搭建todolist待办事项来学习组件与js之间的交互
- layui 行悬停显示工具_Minitab | 工具栏和状态栏
- 算法测试—机器学习算法评价指标
- 小程序WXML基本使用
- PrimeFaces Mobile入门
- Leetcode836.Rectangle Overlap矩阵重叠
- SQL语法集锦一:SQL语句实现表的横向聚合
- centos7 mysql 数据库备份与还原
- php 调试环境配置
- 中国公用计算机互联网网络简称为什么,中国公用计算机互联网国际联网管理办法...
- Pwned Vulnhub
- CSharpGL(13)用GLSL实现点光源(point light)和平行光源(directional light)的漫反射(diffuse reflection)...
- Application provided invalid, non monotonically increasing dts to muxer in stream
- Python实现二维码、条形码识别
- java异常之-ClassNotFoundException: .......web.context.ContextLoaderServlet
- 启发式算法,元启发式算法,超启发式算法
- POJ3984迷宫问题
- vr分类及其常见类型
- 测试之道——阿里巴巴八年测试专家倾情奉献
热门文章
- java上传加密_Java上传下载文件并实现加密解密
- 缓存成神路:Redis读写分离难以理解?一文解析Redis读写分离技术
- 力扣(LeetCode)刷题,简单题(第16期)
- 【图像分类案例】(2) DenseNet 天气图片四分类(权重迁移学习),附Tensorflow完整代码
- mysql通过参数文件启动_mysql启动的时参数文件中的[mysql]下的参数没有生效
- 【小心勿喷,吃饭中的小朋友最好别看】史上最搞笑的前端vue文件命名,没有之一,呵呵哒
- 用Python和项目进行机器学习(初学者) Machine Learning A-Z with Python with Project (Beginner)
- visual-reasoning 笔记
- 开源分布式Job系统,调度与业务分离-如何创建一个计划HttpJob任务
- Sql语法---DDL