求解最长回文子串时触发AddressSanitizer堆缓冲区溢出错误
问题背景
我在LeetCode上解决最长回文子串问题时,代码在DEV-C++中运行正常,但提交到LeetCode后触发了「AddressSanitizer: heap-buffer-overflow on address」运行时错误。ChatGPT修改后的代码几乎没做明显改动却能正常运行,想知道问题出在哪。
我的代码
char* longestPalindrome(char* s) { int i = 0; int j = 0; int high_i = 0; int high_j = 0; int maxlen = 0; int leng = strlen(s); for(i = 0; i < strlen(s); i++) { for(j = 0; s[i - j] == s[i + j] && (i - j) >= 0 && i + j < leng ; j++) // for odd number length palindromes {// checking index is not out of range if(((j >= high_j))) { high_j = j; high_i = i; maxlen = (2 * high_j) + 1; } } } for(i = 0; i < leng; i++) { for(j = 0; s[i - j] == s[i + j + 1] && (i - j) >= 0 && i + j < leng - 1; j++) // for even number length substrings { // making sure index is not out of range if((j + 1) * 2 > maxlen) // compare palindrome length { high_j = j; high_i = i; maxlen = (1 + j) * 2; } } } char* result = malloc(sizeof(char) * (maxlen + 1)); // allocate memory for return string strncpy(result, s + high_i - high_j, maxlen); // start copying (i - j) to maxlen to result string result[maxlen] = '\0'; // end null end of string return result; }
ChatGPT修正后的代码
char* longestPalindrome(char* s) { int i = 0; int j = 0; int high_i = 0; int high_j = 0; int maxlen = 0; int leng = strlen(s); for (i = 0; i < leng; i++) { for (j = 0; (i - j) >= 0 && (i + j) < leng && s[i - j] == s[i + j]; j++) { if (j >= high_j) { high_j = j; high_i = i; maxlen = (2 * high_j) + 1; } } } for (i = 0; i < leng; i++) { for (j = 0; (i - j) >= 0 && (i + j + 1) < leng && s[i - j] == s[i + j + 1]; j++) { if ((j + 1) * 2 > maxlen) { high_j = j; high_i = i; maxlen = (j + 1) * 2; } } } char* result = (char*)malloc(sizeof(char) * (maxlen + 1)); strncpy(result, s + high_i - high_j, maxlen); result[maxlen] = '\0'; return result; }
错误日志
================================================================= ==22==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000000f at pc 0x55de54d7590e bp 0x7ffea23781a0 sp 0x7ffea2378190 READ of size 1 at 0x60200000000f thread T0 #2 0x7f8076b44082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) 0x60200000000f is located 1 bytes to the left of 6-byte region [0x602000000010,0x602000000016) allocated by thread T0 here: #0 0x7f807778c808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144 #3 0x7f8076b44082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) Shadow bytes around the buggy address: 0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 =>0x0c047fff8000: fa[fa]06 fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==22==ABORTING
问题根源分析
核心问题出在条件判断顺序以及循环边界逻辑错误,直接导致了内存越界访问:
内层循环条件顺序错误
原代码在判断回文时,先执行s[i-j] == s[i+j]或s[i-j] == s[i+j+1]的内容比较,再检查索引是否合法。当i-j为负数或i+j超出字符串长度时,已经触发了越界内存访问——这正是AddressSanitizer检测到的heap-buffer-overflow的直接原因。
修正后的代码将索引合法性判断放在内容比较之前,只有在索引合法的前提下才会访问字符串对应位置,彻底避免了越界。偶数长度回文的边界判断错误
原代码偶数长度回文的内层循环条件写为i + j < leng - 1,逻辑存在漏洞,正确的边界判断应该是i + j + 1 < leng(确保右边界不越界),修正后的代码修复了这一点。次要效率问题
原代码第一个外层循环使用i < strlen(s),每次循环都会调用strlen,虽然不直接导致崩溃,但会降低代码效率,修正后的代码改用提前计算好的leng变量。malloc的类型转换
修正后的代码给malloc添加了(char*)强制类型转换,这在C++环境下是必要的,但在C环境下不是必须的,不属于崩溃的原因。
总结
DEV-C++默认未开启内存越界检测,因此代码的潜在问题没有暴露;而LeetCode的AddressSanitizer会严格检测这类不安全的内存访问行为。崩溃的核心原因是先访问内存再检查索引合法性的错误逻辑,修正后即可正常运行。
内容的提问来源于stack exchange,提问作者bleckemr

