一个include引起的惨案

0x00 前言

emmmmm 前一段时间部门转iot了,然后就开始做相关的东西了,写了一些c语言的东西,踩到一个一开始觉得有点玄学的坑.在arm64的平台上遇到了一个比较奇怪的bug.

0x01 起因

我的代码中有大概这样一个操作

1
struct test *t = my_malloc(sizeof(struct test));

my_malloc大概是malloc一个封装,代码大致是这样,它属于my_stdlib.h

1
2
3
void* my_malloc(param){
return malloc(param);
}

然后就碰上一个有些奇怪的问题,调用my_malloc以后,返回的地址,访问时会报段错误,而实际查看它的内存信息的时候,像是被覆盖了.

1
2
3
4
5
实际malloc返回的地址
7f968511e0

而my_malloc返回后的地址变成了这样
ffffffff968511e0

大致情况看起来就是,前四个字节,像是被什么不知名的东西,覆盖掉了.

0x02 分析

起初确实是觉得是代码逻辑有什么问题,导致了溢出之类的,覆盖了这一部分的返回值,而后发现斌没有,在尝试使用gdb的单步调试的时候发现了更加奇怪的情况.

在bl跳转调用my_malloc后,其中bl跳转调用真正的malloc,以及最后my_malloc返回前,我都查看了x0寄存器的值,发现都没有问题,而后在my_malloc返回,走到外部调用它的函数的时候,返回值像是突然发生了变化,停在调用bl my_malloc之后时,查看x0寄存器,它的值仍然是正常了,尝试使用gdb修改pc指针,跳过bl后的语句,结果发现,返回给外部函数的值就正常了.

造成异常的语句,是一条汇编代码

1
sxtw	x0, w0

它的作用是将w0的值进行符号扩展,再赋值给x0寄存器,w0为x0的低32位,sxtw的作用大致是在扩展为64位时,将原先符号位前面的数据填满左边,而零填满右边的数据.(我目前汇编这个不是太好,这里可能没有很透彻的理解这条汇编,具体可以看这里https://developer.arm.com/docs/100898/latest/data-processing-instructions)

这里大概分析一下这条语句在目前这个场景下实际执行的情况

1
2
3
4
5
6
7
8
9
10
11
12
首先x0原先的值,在调用完my_malloc的之后,变成了

高32位 低32位
0000007f 968511e0

而w0此时的值,即为x0的底32位
968511e0

sxtw将w0的符号位扩展,填满前32位,所以结果变成了这样

高32位 低32位
ffffffff 968511e0

到这里,就已经分析出来了报错的原因,即为多出来的这一条sxtw语句,导致了返回值变为了一个奇怪的值,不可访问,而后导致段错误,但是真正问题是,为什么会多出一条sxtw语句导致这样的问题.

0x03 原因

找了一堆资料,最后发现..出现这个情况的原因,比较沙雕. 我找到一份ppt,其中稍微提到了一个这样的情况.

这里大概解释一下,就是当没有函数原型的时候,编译出来的返回值的类型会是int,也就是四个字节,也就是底32位,当它存在一个64的寄存器的时候,可能就需要进行符号扩展的操作,也就是使用功能类似sxtw的语句,导致了我上面提到的那个问题,而后引入了my_stdlib.h,就没有这个问题了.

0x04 总结

总结就是,以后写代码还是得多注意编译器警告.这个玩意在编译的时候其实有警告信息的,我当时看到编译通过了就没仔细看,今天和同事聊了一下这个事情,我们决定在编译期间,把警告直接当作报错停止编译过程了.不过讲道理我以前连gdb都不怎么会用,经过这一波应该也算是会点新东西了,嗨呀我好菜啊.