gdb调试时出现glibc detected *** free(): invalid pointer: 0x000000000060101c

当程序在堆上动态分配内存并忘记或多次释放时,可能会遇到glibc检测到的`free(): invalid pointer`错误。本文探讨了如何通过gdb调试此类问题,以及内存泄漏和重复释放内存的危害。强调了动态内存管理的重要性,即正确地申请和释放内存以避免未定义行为和内存泄漏。

程序的内存空间分为全局变量区,栈空间,堆空间等多部分,通常栈空间都不大,几M的大小,开一个长度为几十万的整型数组,栈空间就受不了,要告诉你stackflow了。
使用malloc函数动态申请堆空间内存会是一个好的解决方案,但要时刻牢记配套的free对动态内存进行释放

#edfine ARRAYLENGTH 100000
int *ia = (int*) malloc(sizeof(int)*ARRAYLENGTH);
free(ia);
ia = NULL;

在堆上申请二维数组的内存空间,也比较简单

int (*ia2)[4] = (int (*) [4])malloc(sizeof(int)*4*ARRAYLENGTH);
for(int i=0;i<4;++i)
{
	free(ia2[i];
	ia2[i] = NULL;
}

按照我的理解,ia2是指针的数组,那肯定需要挨个进行释放,才能保证正确释放动态内存,但事实上这一段程序出现了一个问题。在用gdb进行调试时,出现了*** glibc detected *** /home/EngzSinger/test: free(): invalid pointer: 0x000000000060101c ***的错误,从这里我们可以得知,程序中我们使用了已经被释放的指针作为参数调用free

[EngzSinger@master]$ gdb ./test
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/EngzSinger/test...done.
(gdb) l
1	#include <stdlib.h>
2	#include <stdio.h>
3	
4	int main()
5	{
6		int (*ia)[3] = (int(*)[3])malloc(sizeof(int)*3*9);
7		int (*ia1)[3];
8		for(int i=0;i<3;++i)
9		{
10			free(ia[i]);
(gdb) l
11			ia[i]=NULL;
12		}
13		return 0;
14	}
(gdb) b 12
Breakpoint 1 at 0x40054c: file free_test.c, line 12.
(gdb) r
Starting program: /home/EngzSinger/test 
*** glibc detected *** /home/EngzSinger/test: free(): invalid pointer: 0x000000000060101c ***
======= Backtrace: =========
/lib64/libc.so.6[0x341b675dee]
/lib64/libc.so.6[0x341b678c3d]
/home/EngzSinger/test[0x400542]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x341b61ed1d]
/home/EngzSinger/test[0x400449]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:11 26918960                           /home/EngzSinger/test
00600000-00601000 rw-p 00000000 08:11 26918960                           /home/EngzSinger/test
00601000-00622000 rw-p 00000000 00:00 0                                  [heap]
341b200000-341b220000 r-xp 00000000 08:03 124518487                      /lib64/ld-2.12.so
341b420000-341b421000 r--p 00020000 08:03 124518487                      /lib64/ld-2.12.so
341b421000-341b422000 rw-p 00021000 08:03 124518487                      /lib64/ld-2.12.so
341b422000-341b423000 rw-p 00000000 00:00 0 
341b600000-341b78a000 r-xp 00000000 08:03 124518610                      /lib64/libc-2.12.so
341b78a000-341b98a000 ---p 0018a000 08:03 124518610                      /lib64/libc-2.12.so
341b98a000-341b98e000 r--p 0018a000 08:03 124518610                      /lib64/libc-2.12.so
341b98e000-341b990000 rw-p 0018e000 08:03 124518610                      /lib64/libc-2.12.so
341b990000-341b994000 rw-p 00000000 00:00 0 
341fa00000-341fa16000 r-xp 00000000 08:03 124518975                      /lib64/libgcc_s-4.4.7-20120601.so.1
341fa16000-341fc15000 ---p 00016000 08:03 124518975                      /lib64/libgcc_s-4.4.7-20120601.so.1
341fc15000-341fc16000 rw-p 00015000 08:03 124518975                      /lib64/libgcc_s-4.4.7-20120601.so.1
7ffff0000000-7ffff0021000 rw-p 00000000 00:00 0 
7ffff0021000-7ffff4000000 ---p 00000000 00:00 0 
7ffff7fd9000-7ffff7fdc000 rw-p 00000000 00:00 0 
7ffff7ffc000-7ffff7ffe000 rw-p 00000000 00:00 0 
7ffff7ffe000-7ffff7fff000 r-xp 00000000 00:00 0                          [vdso]
7ffffffe9000-7ffffffff000 rw-p 00000000 00:00 0                          [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Program received signal SIGABRT, Aborted.
0x000000341b632495 in raise () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.209.el6_9.2.x86_64 libgcc-4.4.7-18.el6.x86_64

进一步的使用valgrind对程序进行内存监控,能发现total heap usage: 1 allocs, 9 frees, 108 bytes allocated,对动态内存进行了一次分配,进行了3次释放,由此造成了错误。

[EngzSinger@master]$ valgrind ./test
==22495== Memcheck, a memory error detector
==22495== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==22495== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==22495== Command: ./test
==22495== 
==22495== Invalid free() / delete / delete[] / realloc()
==22495==    at 0x4A06430: free (vg_replace_malloc.c:446)
==22495==    by 0x400541: main (free_test.c:10)
==22495==  Address 0x4c3104c is 12 bytes inside a block of size 108 free'd
==22495==    at 0x4A06430: free (vg_replace_malloc.c:446)
==22495==    by 0x400541: main (free_test.c:10)
==22495== 
==22495== 
==22495== HEAP SUMMARY:
==22495==     in use at exit: 0 bytes in 0 blocks
==22495==   total heap usage: 1 allocs, 3 frees, 108 bytes allocated
==22495== 
==22495== All heap blocks were freed -- no leaks are possible
==22495== 
==22495== For counts of detected and suppressed errors, rerun with: -v
==22495== ERROR SUMMARY: 8 errors from 1 contexts (suppressed: 8 from 6)

造成这个错误的原因就是重复释放了动态内存,在使用动态内存的时候一定要牢记: malloc申请的动态内存一定要有配套的free进行正确释放

  • 如果忘记释放,会造成内存泄漏
  • 如果释放后仍使用指针访问相应的内存,则会造成未定义的行为
<think> 我们面对的是两个错误: 1. 运行错误:'*** stack smashing detected ***: terminated' 2. 构建错误:'make[1]: *** [Makefile:37: all] Aborted (core dumped)' 首先分析错误原因: 1. **stack smashing detected**:这是GCC的栈保护机制(-fstack-protector)检测到栈溢出(stack overflow)或栈破坏(stack corruption)触发的错误。常见原因包括: - 数组越界访问(写入超出数组边界) - 使用未初始化的指针 - 缓冲区溢出(如strcpy等不安全函数) - 局部变量被意外修改 2. **make[1]: *** [Makefile:37: all] Aborted (core dumped)**:表示在构建过程中,某个子进程(make[1])执行的目标(在Makefile第37行的'all'目标)因程序崩溃(Aborted)而失败,并且生成了core dump文件。 这两个错误通常关联:构建过程中运行的某个程序(可能是编译生成的测试程序或工具)发生了栈破坏,导致程序崩溃,进而导致构建失败。 ### 解决步骤 #### 步骤1:定位崩溃位置 - 使用core dump文件定位崩溃位置(如果已生成) 1. 确保系统允许生成core dump文件(检查ulimit -c,若为0则运行`ulimit -c unlimited`) 2. 在构建目录下查找core文件(通常命名为core或core.<pid>) 3. 使用gdb加载崩溃的程序和core文件: ```bash gdb <你的程序路径> core ``` 4. 在gdb中运行`bt`(backtrace)查看崩溃的调用栈 - 如果未生成core文件,尝试直接运行崩溃的程序(通常在构建过程中运行的程序,可能是测试程序或工具),观察是否重现崩溃。 #### 步骤2:检查代码中的常见错误 根据经验,栈破坏通常由以下原因引起: 1. **数组越界**:检查所有数组访问,特别是循环边界条件。 2. **缓冲区溢出**:检查字符串操作(如strcpy, sprintf等),使用更安全的替代函数(strncpy, snprintf)。 3. **指针错误**:检查指针是否被正确初始化,是否在释放后继续使用,或是否越界访问。 4. **内存重叠**:检查memcpy是否在重叠内存区域使用(用memmove代替)。 5. **局部变量返回**:检查是否返回局部变量的地址(局部变量在函数返回后失效)。 #### 步骤3:使用调试工具辅助定位 1. **AddressSanitizer (ASan)**:在编译加入ASan选项,可以检测内存错误(包括栈溢出)。 在gcc/clang中添加编译选项: ```makefile CFLAGS += -fsanitize=address -g LDFLAGS += -fsanitize=address ``` 重新编译运行,ASan会在错误发生打印详细的错误位置和调用栈。 2. **Valgrind**:动态分析工具,检测内存错误。 ```bash valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./your_program ``` #### 步骤4:检查构建过程中的工具 构建错误发生在Makefile第37行(目标'all'),检查该目标执行的命令: - 查看Makefile第37行及附近,确定正在执行什么命令(可能是编译、链接或运行测试)。 - 如果该命令是运行测试程序,则问题出在测试程序本身(按照步骤1-3调试)。 - 如果该命令是编译或链接,则可能是编译器本身崩溃(较罕见),尝试升级编译器。 #### 步骤5:栈保护机制相关选项 如果问题难以定位,可以尝试临关闭栈保护(仅用于调试,不推荐用于发布): ```makefile CFLAGS += -fno-stack-protector ``` 重新构建并运行,观察是否崩溃。如果不再崩溃,则确认为栈溢出问题,但仍需定位具体位置。 ### 示例调试过程(假设崩溃发生在测试程序) 1. 在Makefile中找到运行测试程序的命令(例如:`./test_program`) 2. 启用ASan重新编译测试程序(修改Makefile): ```makefile CFLAGS = -g -fsanitize=address LDFLAGS = -fsanitize=address ``` 3. 重新构建并运行测试: ```bash make clean make ``` 4. 当崩溃发生,ASan会输出类似以下信息: ``` ==12345==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd12345678 ... WRITE of size 4 at 0x7ffd12345678 thread T0 #0 0x400b2d in my_function source.c:10 #1 0x400c11 in main source.c:20 ``` 根据提示的源文件位置(source.c:10)检查代码。 ### 常见修复案例 案例1:数组越界 ```c // 错误代码 int arr[10]; for (int i = 0; i <= 10; i++) { // 越界访问arr[10] arr[i] = i; } ``` 案例2:使用strcpy导致缓冲区溢出 ```c // 错误代码 char dest[10]; strcpy(dest, "this string is too long"); // 超出10字节 // 修复:使用strncpy并确保截断后以'\0'结尾 strncpy(dest, "this string is too long", sizeof(dest)-1); dest[sizeof(dest)-1] = '\0'; ``` 案例3:返回局部数组 ```c // 错误代码 char* get_buffer() { char buf[100]; return buf; // 返回局部变量地址 } // 修复:使用静态数组或动态分配 ``` ### 注意事项 - 栈保护(stack smashing)错误通常在崩溃点之后才被检测到,因此崩溃位置可能不是问题根源。 - 多线程环境下栈破坏可能由竞态条件引起,需检查线程同步。 - 如果崩溃发生在第三方库中,考虑升级库或检查调用方式。 ### 总结流程 1. 利用core dump和gdb定位崩溃点 2. 使用ASan或Valgrind进行内存错误检测 3. 检查代码中的常见内存错误模式 4. 修复问题后重新测试 通过以上方法,通常能够解决栈破坏导致的崩溃问题[^2][^3]。 §§相关问题§§ 1. 如何生成和分析core dump文件? 2. AddressSanitizer(ASan)能检测哪些类型的内存错误? 3. 如何避免C/C++中的缓冲区溢出? </think>要解决 `*** stack smashing detected ***: terminated` 和 `make[1]: *** [Makefile:37: all] Aborted (core dumped)` 错误,需要分析两者关联并采取系统化调试措施。以下是完整解决方案: ### 一、问题原因分析 1. **栈破坏检测 (stack smashing detected)** - 由GCC的栈保护机制(`-fstack-protector`)触发 - 表明程序发生了**缓冲区溢出**或**栈内存破坏** - 常见原因:数组越界、指针错误、内存操作越界[^2] 2. **Makefile构建失败** - `Makefile:37: all` 表示第37行的`all`目标执行失败 - `Aborted (core dumped)` 表明构建过程中某个程序崩溃并生成core文件 - 通常是编译/测试阶段的可执行文件触发了栈破坏 ### 二、解决步骤 #### 1. 获取核心转储文件 ```bash # 确保系统允许生成core文件 ulimit -c unlimited # 重新运行make(失败后会生成core文件) make -B ``` #### 2. 使用GDB分析core文件 ```bash # 定位崩溃程序(通常在Makefile第37行附近) grep -n "all:" Makefile # 找到all目标 # 加载core文件分析 gdb <崩溃程序路径> core # 或 core.<pid> ``` 在GDB中执行: ```gdb # 查看崩溃栈帧 (gdb) bt full # 检查寄存器状态 (gdb) info registers # 反汇编当前函数 (gdb) disassemble ``` #### 3. 启用内存调试工具(重新编译) 在Makefile中添加调试选项: ```makefile # 修改Makefile第37行附近的编译标志 CFLAGS += -g -fsanitize=address -fno-omit-frame-pointer LDFLAGS += -fsanitize=address ``` #### 4. 常见错误点检查 在代码中排查以下高危操作: ```c // 检查1:数组越界访问 char buf[10]; for(int i=0; i<=10; i++) { // 错误:i<=10导致越界 buf[i] = 0; // 应改为 i<10 } // 检查2:不安全的字符串操作 char dest[10]; strcpy(dest, source); // 错误:未检查长度 // 修复:使用strncpy(dest, source, sizeof(dest)-1); // 检查3:指针操作越界 int *arr = malloc(10*sizeof(int)); arr[10] = 42; // 错误:越界访问 ``` #### 5. 使用Valgrind动态检测 ```bash # 通过Makefile运行内存检测 make clean valgrind --leak-check=full --track-origins=yes make ``` ### 三、专项解决方案 #### 案例1:栈保护机制触发 ```diff // 在代码可疑区域添加诊断 #include <stdio.h> void vulnerable_func() { char buf[10]; - gets(buf); // 高危函数 + if(fgets(buf, sizeof(buf), stdin) == NULL) { + perror("Input error"); + exit(1); + } } ``` #### 案例2:Makefile构建失败修复 ```makefile # 修改Makefile第37行附近 all: main # 添加诊断命令 @echo "Checking memory safety..." -valgrind --error-exitcode=1 ./$< # 失败返回错误码 @echo "Build successful!" ``` ### 四、进阶调试技巧 1. **栈保护选项控制**: ```bash # 临禁用栈保护(仅调试) CFLAGS += -fno-stack-protector ``` 2. **生成内存映射文件**: ```bash # 在Makefile中添加 LDFLAGS += -Wl,-Map=output.map ``` 3. **代码插桩检测**: ```c #define GUARD_SIZE 32 uint8_t __attribute__((aligned(32))) guard[GUARD_SIZE] = {0xAA}; void check_stack() { for(int i=0; i<GUARD_SIZE; i++) { if(guard[i] != 0xAA) { fprintf(stderr, "Stack corruption detected!\n"); abort(); } } } ``` ### 五、预防措施 1. **代码规范**: - 使用安全函数(`snprintf`替代`sprintf`) - 指针操作前检查边界 - 启用编译器警告:`-Wall -Wextra -Werror` 2. **构建系统加固**: ```makefile # 在Makefile中添加安全编译标志 HARDEN_FLAGS := -D_FORTIFY_SOURCE=2 -fstack-protector-strong -Wformat-security CFLAGS += $(HARDEN_FLAGS) ``` 3. **自动化检测**: ```bash # 添加静态分析到构建流程 scan-build make ``` > **关键提示**:栈破坏错误通常在崩溃点之后才被检测到,实际错误位置可能在崩溃前的代码中。建议使用二分法(注释代码块)逐步缩小范围[^1][^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值