转载于https://www.csdn.net/tags/NtDaUgzsNjgyMzUtYmxvZwO0O0OO0O0O.html
文章目录
- 常见的程序崩溃示例
- 执行结果分析
- 常见的程序崩溃示例
常见的崩溃类型有以下几种:
对空指针指向的内存非法写操作
对空指针指向的内存非法读操作
除0操作
大的临时变量或者递归等导致栈溢出
对只读内存进行写操作
堆越界写操作
对已经释放的内存进行写操作
代码示例:
//corruption.c
#include <stdlib.h>
#include <stdio.h>
void make_corruption(int type)
{
printf("case %d:\n", type);
switch (type)
{
case 0:
{
//对空指针指向的内存非法写操作
char *str = NULL;
str[0] = 0;
break;
}
case 1:
{
//对空指针指向的内存非法读操作
char *str = NULL;
int i = str[0];
printf("i=%d", i);
break;
}
case 2:
{
//除0操作
int i = 10;
int j = 0;
int ret = i / j;
printf("ret=%d", ret);
break;
}
case 3:
{
//大的临时变量导致栈溢出
//char str[8 * 1024 * 1024] = {0}; //需要验证时再放开该行,否则栈越界必现死机导致其他case无法验证
break;
}
case 4:
{
//对只读内存进行写操作
char *str = "abc";
str[0] = 10;
break;
}
case 5:
{
//堆越界写操作
int *a = (int *)malloc(sizeof(int) * 4);
int *b = (int *)malloc(sizeof(int) * 4);
printf("a:%p, b:%p\n", a, b);
memset(a, 0, sizeof(int) * 8);
free(a);
free(b);
break;
}
case 6:
{
//对已经释放的内存进行写操作。
#if 0
//没有死机
char *str = (char *)malloc(SMALL_MEMORY);
free(str);
memset(str, 0, SMALL_MEMORY);
#else
//coredump
char *str = (char *)malloc(LARGE_MEMORY);
free(str);
memset(str, 0, LARGE_MEMORY);
#endif
break;
}
default:
{
printf("to do.\n");
}
}
return;
}
int main()
{
int type = 0;
scanf("%d", &type);
//制造程序崩溃
make_corruption(type);
return 0;
}
- 执行结果分析
[root@localhost all_kinds_corrupt]# gcc -g corruption.c -o corrupt
[root@localhost all_kinds_corrupt]# ./corrupt
0
case 0:
Segmentation fault (core dumped)
[root@localhost all_kinds_corrupt]# ./corrupt
1
case 1:
Segmentation fault (core dumped)
[root@localhost all_kinds_corrupt]# ./corrupt
2
case 2:
Floating point exception (core dumped)
[root@localhost all_kinds_corrupt]# ./corrupt
4
case 4:
Segmentation fault (core dumped)
[root@localhost all_kinds_corrupt]# ./corrupt
5
case 5:
a:0x1ca9ac0, b:0x1ca9ae0
free(): invalid pointer
Segmentation fault (core dumped)
[root@localhost all_kinds_corrupt]# ./corrupt
6
case 6:
我们可以看到,case 6在两种不同内存大小情况下表现一同:
(1)小内存情况下没有死机,这是因为Glibc会将该内存放到unsorted bin,此时内存没有进行合并或者重新分配出去,可以进行写操作。
(2)对于大内存情况,Glibc会通过调用mmap(大于128K)从系统申请内存,在free之后会直接归还给操作系统,此时进行写操作是非法的。
至于Glibc对于不同大小的内存管理,则需要另外一篇文章来进行详细讲解了。
我们把case 3的代码放开,重新编译执行:
[root@localhost all_kinds_corrupt]# ./corrupt
100
段错误 (核心已转储)
type=100时应该是走进default分支,但实际上程序产生了段错误,其原因就是栈溢出。我们可以通过ulimit -s查看栈的默认大小,通常为8M。
[root@localhost all_kinds_corrupt]# ulimit -s
8192