LeetCode 125有效回文题:代码切换后崩溃问题求助
LeetCode 125题:验证有效回文代码崩溃原因分析
问题场景
我在解决LeetCode第125题「验证有效回文」时遇到问题:启用代码中#if 0块的逻辑时程序触发**堆缓冲区溢出(heap-buffer-overflow)**崩溃,但改用#else块逻辑后代码可正常运行。需求是判断原字符串剥离出仅含[a-z][0-9]的子串是否为回文,现分析崩溃原因。
代码片段
// https://leetcode.com/problems/valid-palindrome/ // Leetcode-125 // Helperfunction to find if a palindrome bool isPal(char *s , int count) { int iterations = (count/2)-1; for (int i = 0; i <= iterations; i++) { if (s[i] != s[count-1-i]) { return false; } } return true; } // function that Leetcode's main calls. bool isPalindrome(char * s) { int i = 0, j=0; int count = 0; if (!s) return false; // Count number of valid characters. Replace invalid characters with 0xff. // Change Upper to lower case numbers while (s[i] != '\0') { // [a-z][0-9] if ((s[i] >= '0' && s[i] <='9') || (s[i] >= 'a' && s[i] <='z')) { count++; } else { if ((s[i] >= 'A' && s[i] <='Z')) { count++; s[i] = s[i] + ('a' - 'A'); } else { s[i] = 0xff; } } i++; } if (!count) return true; char *stripped = (char *) malloc ((count+1) * sizeof(char)); i = 0; #if 0 // Enabling this causes a crash while(s[i] != '\0') { if(s[i] != 0xff) { stripped[j] = s[i]; j++; } i++; } #else while(s[i] != '\0') { if ((s[i] >= '0' && s[i] <='9') || (s[i] >= 'a' && s[i] <='z')) { stripped[j] = s[i]; j++; } i++; } #endif stripped[count] = '\0'; return isPal(stripped, count); }
崩溃原因分析
核心问题:修改只读内存引发未定义行为
LeetCode传入的字符串参数大概率是只读内存(如字符串字面量),这类内存存储在程序的只读数据段,不允许修改。但代码的第一个循环中存在两处修改原字符串的操作:
- 将大写字母转换为小写时,直接修改
s[i] - 将无效字符设为
0xff时,直接修改s[i]
这种修改只读内存的行为属于未定义行为,可能会破坏内存结构,导致后续对堆内存stripped的写入操作触发堆缓冲区溢出。
为什么#else块暂时正常?
#else块的逻辑虽然也依赖修改后的s[i],但可能因测试用例的特殊性(如输入字符串不在只读内存)或编译器优化,暂时未触发崩溃,但本质上这种修改原字符串的做法仍然是不安全的,存在潜在风险。
次要风险:0xff的符号歧义
若编译器将char视为有符号类型,0xff会被解释为-1。如果原字符串中存在值为-1的扩展ASCII字符,会被误判为无效字符,但这不是本次崩溃的直接原因,只是代码的潜在漏洞。
修复方案
避免修改原字符串,改为在复制时直接判断并转换字符:
- 统计有效字符数量时,仅计数不修改原字符串
- 复制有效字符时,实时判断字符类型并转换为小写
修改后的核心代码片段:
// 仅统计有效字符数量,不修改原字符串 while (s[i] != '\0') { if ((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >= 'A' && s[i] <= 'Z')) { count++; } i++; } // 复制并转换有效字符 i = 0; j = 0; while (s[i] != '\0') { if (s[i] >= '0' && s[i] <= '9') { stripped[j++] = s[i]; } else if (s[i] >= 'a' && s[i] <= 'z') { stripped[j++] = s[i]; } else if (s[i] >= 'A' && s[i] <= 'Z') { stripped[j++] = s[i] + ('a' - 'A'); } i++; }
内容的提问来源于stack exchange,提问作者Kingkong Jnr
相关产品推荐
相关产品推荐

