`realloc():尝试处理未知大小的输入时,下一个大小无效`
我有以下代码: 如果传递了`realloc():尝试处理未知大小的输入时,下一个大小无效`,c,realloc,C,Realloc,我有以下代码: 如果传递了NULL,则函数get\u unlimited\u input将分配一个新字符串,否则它将只向现有字符串追加字符。 最后,它会截断多余的字节。 (DEFAULT\u BUFFER\u SIZE被设置为5以测试多次重新分配的情况) #包括 #包括 #包括 #定义默认缓冲区大小5 字符*获取\无限制\输入(字符*缓冲区){ 大小\u t当前大小; if(buffer==NULL){ buffer=malloc(默认缓冲区大小*sizeof(char)); 当前缓冲区大小=默
NULL
,则函数get\u unlimited\u input
将分配一个新字符串,否则它将只向现有字符串追加字符。
最后,它会截断多余的字节。
(DEFAULT\u BUFFER\u SIZE
被设置为5
以测试多次重新分配的情况)
#包括
#包括
#包括
#定义默认缓冲区大小5
字符*获取\无限制\输入(字符*缓冲区){
大小\u t当前大小;
if(buffer==NULL){
buffer=malloc(默认缓冲区大小*sizeof(char));
当前缓冲区大小=默认缓冲区大小;
}否则{
当前缓冲区大小=strlen(缓冲区)+默认缓冲区大小;
}
char*cursor=缓冲区+当前大小-默认缓冲区大小;
对于(;;){
int current=getchar();
*游标=(字符)当前;
游标++;
如果(当前=='\n'| |当前==EOF)
打破
如果(光标>=缓冲区+当前大小){
当前缓冲区大小+=默认缓冲区大小;
缓冲区=realloc(缓冲区,当前_大小);
游标=缓冲区+当前大小-默认缓冲区大小;
}
}
*游标='\0';
缓冲区=realloc(缓冲区,光标-缓冲区);
返回缓冲区;
}
int main(){
printf(“>”);
char*buffer=get_unlimited_input(NULL);
printf(“>”);
获取不受限制的输入(缓冲区);
}
在大多数情况下,它工作正常,但如果我先通过117个字符,然后通过12个字符,它就会崩溃:
>.....................................................................................................................
>............
realloc(): invalid next size
Aborted (core dumped)
问题出在哪里?除其他问题外,在返回缓冲区之前,请先修剪缓冲区中的所有多余空间。但是,如果将缓冲区传递给函数,则假定它仍然有额外的空间。因此,您不能将从函数返回的缓冲区传递回函数。但你就是这么做的
} else {
current_size = strlen(buffer) + DEFAULT_BUFFER_SIZE;
}
...
buffer = realloc(buffer, cursor - buffer);
另外,正如KamilCuk所指出的,在返回的字符串中没有为终止符留出空间,因此在其上调用strlen
是不安全的
您应该记录对函数输入的要求以及函数输出保证满足的要求。这样就更容易发现这样的错误
当您看到“如果一个缓冲区被传递给这个函数,它必须有额外的空间”和“从这个函数返回的缓冲区没有任何额外的空间”,很明显,您无法将返回的缓冲区传递回函数,因为输出保证不满足输入要求。基本问题是,当您使用非空指针调用
get_unlimited_input
时(先前调用的预存缓冲区),它假定缓冲区大小为strlen(缓冲区)+默认缓冲区大小
,这是错误的。前一个调用实际上已经重新分配了缓冲区,以匹配不包括终止NUL的字符串长度(这意味着终止NUL本身可能丢失)
您可以通过在存储NUL之后和重新定位之前通过增加游标来修正这些问题(这样realloc将足够大),然后在传递非空指针时将当前_大小的计算更改为strlen(buffer)+1
另一个问题是,当您从getchar获取EOF时,您正在将该EOF强制转换为一个char并将其存储在缓冲区中。EOF不是一个有效的字符——让
getchar
返回int
而不是char
的全部意义在于,它可以将EOF作为不同于任何字符的信号。因此,在EOF中,您正在缓冲区中存储一些随机垃圾字符(或非字符),这些字符可能只是作为垃圾显示,或者可能导致输出崩溃或错误(取决于系统)。如果您从malloc
、realloc
或free
中获得运行时错误,则意味着您已损坏了堆。堆损坏的常见原因包括释放内存块后使用内存块(这包括调用free
两次)和缓冲区溢出(和下溢)
损坏发生在运行时错误之前。这可能发生在很久以前,因此如果您只是在错误发生时开始调试程序,可能很难重建发生的情况。还有其他工具可以帮助您更准确地定位问题。在使用GCC或Clang的类Unix系统上,这是非常有用的
# Settings for Ubuntu 20.04; you may need to adapt for your system
$ export ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-10/bin/llvm-symbolizer
$ gcc -O -Wall -Wextra a.c -fsanitize=address,undefined && python3 -c "print('.'*117+'\n'+'.'*12)" | ./a.out
=================================================================
==446177==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000236 at pc 0x7f246fc0ea6d bp 0x7ffd4e309380 sp 0x7ffd4e308b28
READ of size 119 at 0x60c000000236 thread T0
#0 0x7f246fc0ea6c (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c)
#1 0x55c04dfb32e7 in get_unlimited_input (.../65891246/a.out+0x12e7)
#2 0x55c04dfb34b1 in main (.../65891246/a.out+0x14b1)
#3 0x7f246f06f0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#4 0x55c04dfb320d in _start (.../65891246/a.out+0x120d)
0x60c000000236 is located 0 bytes to the right of 118-byte region [0x60c0000001c0,0x60c000000236)
allocated by thread T0 here:
#0 0x7f246fcb4ffe in __interceptor_realloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
#1 0x55c04dfb345c in get_unlimited_input (.../65891246/a.out+0x145c)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c)
Shadow bytes around the buggy address:
0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c187fff8000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
0x0c187fff8010: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
0x0c187fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
0x0c187fff8030: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c187fff8040: 00 00 00 00 00 00[06]fa fa fa fa fa fa fa fa fa
0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8090: 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
==446177==ABORTING
你。最重要的部分是堆栈跟踪,以及缓冲区溢出发生在“118字节区域”上的指示,这表明它发生在第一次调用get_unlimited_input
的末尾或第二次调用的开头。堆栈跟踪提供发生溢出的确切代码地址,可用于在调试器中设置断点;您将看到它接近函数的末尾。正如其他人已经指出的那样
*cursor = '\0';
buffer = realloc(buffer, cursor - buffer);
错误:您没有为'\0'
终止符留出空间。你需要
*(cursor++) = '\0';
buffer = realloc(buffer, cursor - buffer);
或
我还没有检查其他bug()。您的代码中存在多个问题,导致堆损坏,如诊断所示:
- 您对当前分配大小的假设不正确:
current\u size=strlen(buffer)+DEFAULT\u buffer\u size代码>太乐观了。由于在返回缓冲区之前将其重新分配到
字节,因此字符串末尾没有松弛cursor-buffer
- 将字节存储到数组后,测试
和'\n'
。这可能是换行符的预期行为,但对于不是字符的EOF
,这是不正确的EOF
- 使用
也不正确:buffer=realloc(buffer,cursor-buffer)重新分配
buffer
指向空终止符,因此应使用cursor
的大小将空终止符保留在allocursor+1-buffer
*(cursor++) = '\0'; buffer = realloc(buffer, cursor - buffer);
*cursor = '\0'; buffer = realloc(buffer, cursor - buffer + 1);
#include <stdio.h> #include <stdlib.h> #define DEFAULT_BUFFER_SIZE 16 /* use address alignment as incremental size */ char *get_unlimited_input(char *buffer) { size_t current_size, pos; char *p; if (buffer == NULL) { pos = 0; current_size = DEFAULT_BUFFER_SIZE; buffer = malloc(DEFAULT_BUFFER_SIZE); if (buffer == NULL) return NULL; } else { pos = strlen(buffer); current_size = pos + 1; } for (;;) { int c = getchar(); if (c == EOF || c == '\0') break; if (pos + 1 == current_size) { // reallocate the buffer current_size += DEFAULT_BUFFER_SIZE; p = realloc(buffer, current_size); if (p == NULL) break; buffer = p; } buffer[pos++] = (char)c; if (c == '\n') break; } buffer[pos] = '\0'; p = realloc(buffer, pos + 1); return (p != NULL) ? p : buffer; } int main() { printf("> "); char *buffer = get_unlimited_input(NULL); printf("got: %s\n", buffer); printf("> "); get_unlimited_input(buffer); printf("got: %s\n", buffer); return 0; }