gcc整数溢出检测 __mulvsi3 __addvdi3
- __mulvsi3:乘法结果溢出
- __addvdi3:加法结果溢出
问题现象
core调用栈有__mulvsi3 或 __addvdi3
Program received signal SIGABRT, Aborted.
0x00110416 in __kernel_vsyscall ()
(gdb) bt
#0 0x00110416 in __kernel_vsyscall ()
#1 0x00555460 in raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#2 0x00556e28 in abort () at abort.c:88
#3 0x08048603 in __mulvsi3 ()
#4 0x080484cd in my_atoi (s=0x804870c "7") at main.c:12
#5 0x08048555 in main () at main.c:22
(gdb)
问题原因
在编译时加上 -ftrapv 参数 , 运行时检测到溢出后 , 会产生SIGABRT , 进程终止。
-ftrapv 参数会让GCC在生成代码时,对算数运算结果做溢出检测,如果检测到溢出,则自动abort退出程序。
示例
#include <stdio.h>
#include <stdlib.h>
int main(int n, char* args[])
{
long a = atol(args[1]);
long b = atol(args[2]);
long result = a + b;
printf("result: %d\n", result);
return 0;
}
编译 gcc -g main.c -ftrapv -o main
执行 ./main 4611686018427387904 4611686018427387904
,4611686018427387904是个第63位为1的64位数
[root@localhost ~]# ./main 4611686018427387904 4611686018427387904
Aborted (core dumped)
gdb查看原因
[root@localhost ~]# gdb main
Reading symbols from main...done.
(gdb) r 4611686018427387904 4611686018427387904
Starting program: /root/main 4611686018427387904 4611686018427387904
Missing separate debuginfos, use: yum debuginfo-install glibc-2.28-164.el8.x86_64
Program received signal SIGABRT, Aborted.
0x00007ffff7a4237f in raise () from /lib64/libc.so.6
(gdb) bt
0 0x00007ffff7a4237f in raise () from /lib64/libc.so.6
1 0x00007ffff7a2cdb5 in abort () from /lib64/libc.so.6
2 0x0000000000400535 in __addvdi3.cold.0 ()
3 0x0000000000400670 in main (n=3, args=0x7fffffffe3a8) at main.c:8
(gdb) disassemble main # 查看main函数汇编代码,注意__addvdi3
Dump of assembler code for function main:
0x0000000000400626 <+0>: push %rbp
0x0000000000400627 <+1>: mov %rsp,%rbp
0x000000000040062a <+4>: sub $0x30,%rsp
0x000000000040062e <+8>: mov %edi,-0x24(%rbp)
0x0000000000400631 <+11>: mov %rsi,-0x30(%rbp)
0x0000000000400635 <+15>: mov -0x30(%rbp),%rax
0x0000000000400639 <+19>: add $0x8,%rax
0x000000000040063d <+23>: mov (%rax),%rax
0x0000000000400640 <+26>: mov %rax,%rdi
0x0000000000400643 <+29>: callq 0x400520 <atol@plt>
0x0000000000400648 <+34>: mov %rax,-0x8(%rbp)
0x000000000040064c <+38>: mov -0x30(%rbp),%rax
0x0000000000400650 <+42>: add $0x10,%rax
0x0000000000400654 <+46>: mov (%rax),%rax
0x0000000000400657 <+49>: mov %rax,%rdi
0x000000000040065a <+52>: callq 0x400520 <atol@plt>
0x000000000040065f <+57>: mov %rax,-0x10(%rbp)
0x0000000000400663 <+61>: mov -0x10(%rbp),%rsi
0x0000000000400667 <+65>: mov -0x8(%rbp),%rdi
0x000000000040066b <+69>: callq 0x4006a0 <__addvdi3> # 这里并不是直接使用x86的加法指令,而是调用c库封装的加法函数
0x0000000000400670 <+74>: mov %rax,-0x18(%rbp)
0x0000000000400674 <+78>: mov -0x18(%rbp),%rax
0x0000000000400678 <+82>: mov %rax,%rsi
0x000000000040067b <+85>: mov $0x400798,%edi
0x0000000000400680 <+90>: mov $0x0,%eax
0x0000000000400685 <+95>: callq 0x400510 <printf@plt>
0x000000000040068a <+100>: mov $0x0,%eax
0x000000000040068f <+105>: leaveq
0x0000000000400690 <+106>: retq
End of assembler dump.
(gdb) disassemble __addvdi3 # 进一步查看__addvdi3的汇编
Dump of assembler code for function __addvdi3:
0x00000000004006a0 <+0>: endbr64
0x00000000004006a4 <+4>: sub $0x8,%rsp
0x00000000004006a8 <+8>: lea (%rdi,%rsi,1),%rax # 计算a + b的值,结果存在%rax。
0x00000000004006ac <+12>: test %rsi,%rsi
0x00000000004006af <+15>: js 0x4006c8 <__addvdi3+40>
0x00000000004006b1 <+17>: cmp %rax,%rdi # 比较结果与a的值
0x00000000004006b4 <+20>: setg %dl
0x00000000004006b7 <+23>: test %dl,%dl
0x00000000004006b9 <+25>: jne 0x400530 <__addvdi3.cold.0> # 如果结果比a小(溢出),就跳转到__addvdi3.cold.0,它最终会调用abort
0x00000000004006bf <+31>: add $0x8,%rsp
0x00000000004006c3 <+35>: retq
0x00000000004006c4 <+36>: nopl 0x0(%rax)
0x00000000004006c8 <+40>: cmp %rax,%rdi
0x00000000004006cb <+43>: setl %dl
0x00000000004006ce <+46>: jmp 0x4006b7 <__addvdi3+23>
End of assembler dump.