`realloc(): invalid next size` 在尝试处理未知大小的输入时

如何解决`realloc(): invalid next size` 在尝试处理未知大小的输入时

我有以下代码: 如果 get_unlimited_input 被传递,函数 NULL 分配一个新字符串,否则它只是将字符附加到现有字符串。 最后它会截断多余的字节。 (DEFAULT_BUFFER_SIZE 设置为 5 以测试多次重新分配的情况)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DEFAULT_BUFFER_SIZE 5

char *get_unlimited_input(char *buffer) {
    size_t current_size;
    if (buffer == NULL) {
        buffer = malloc(DEFAULT_BUFFER_SIZE * sizeof(char));
        current_size = DEFAULT_BUFFER_SIZE;
    } else {
        current_size = strlen(buffer) + DEFAULT_BUFFER_SIZE;
    }
    char *cursor = buffer + current_size - DEFAULT_BUFFER_SIZE;
    for (;;) {
        int current = getchar();
        *cursor = (char)current;
        cursor++;
        if (current == '\n' || current == EOF)
            break;
        if (cursor >= buffer + current_size) {
            current_size += DEFAULT_BUFFER_SIZE;
            buffer = realloc(buffer,current_size);
            cursor = buffer + current_size - DEFAULT_BUFFER_SIZE;
        }
    }
    *cursor = '\0';
    buffer = realloc(buffer,cursor - buffer);
    return buffer;
}

int main() {
    printf(">");
    char *buffer = get_unlimited_input(NULL);
    printf(">");
    get_unlimited_input(buffer);
}

在大多数情况下它工作得很好,但是如果我先传递 117 个字符,然后传递 12 个它就会崩溃:

>.....................................................................................................................
>............
realloc(): invalid next size
Aborted (core dumped)
python3 -c "print('.'*117+'\n'+'.'*12)" | ./_buffer
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(buffer) + DEFAULT_BUFFER_SIZE,这是错误的。之前的调用实际上会重新分配缓冲区以匹配不包括终止 NUL 的字符串的长度(这意味着终止 NUL 本身很可能会丢失。)

您可以通过在存储 NUL 之后和重新分配之前增加游标来解决这些问题(因此重新分配会足够大),然后在传递非空指针时将 current_size 的计算更改为 strlen(buffer) + 1

另一个问题是,当您从 getchar 获取 EOF 时,您将该 EOF 转换为 char 并将其存储在缓冲区中。 EOF 不是有效字符——让 getchar 返回 int 而不是 char 的全部意义在于它可以将 EOF 表示为与任何字符不同。因此,在 EOF 中,您将一些随机的垃圾字符(或非字符)存储在缓冲区中,这可能只是显示为垃圾,或者可能导致输出崩溃或错误(取决于系统)。

,

如果您收到来自 mallocreallocfree 的运行时错误,则意味着您已损坏堆。堆损坏的常见原因包括在释放内存块后使用它(这包括调用 free 两次)和缓冲区溢出(和下溢)。

损坏发生在运行时错误之前。它可能已经发生了很长时间,所以如果你只是在错误发生的时候开始调试程序,可能很难重建发生了什么。还有其他工具可以帮助您更准确地定位问题。在带有 GCC 或 Clang 的类 Unix 系统上,AddressSanitizer 非常有用。

# 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);

    *cursor = '\0';
    buffer = realloc(buffer,cursor - buffer + 1);

我尚未检查其他错误 (it isn't the only one)。

,

您的代码中存在多个问题,导致堆损坏,如诊断所示:

  • 您对当前分配大小的假设不正确:current_size = strlen(buffer) + DEFAULT_BUFFER_SIZE; 过于乐观。由于您在返回之前将缓冲区重新分配为 cursor - buffer 字节,因此字符串末尾没有松弛。

  • 在将字节存储到数组后测试 '\n'EOF。这可能是换行符的预期行为,但对于不是字符的 EOF 来说是不正确的。

  • buffer 重新分配 buffer = realloc(buffer,cursor - buffer); 也是不正确的:cursor 指向空终止符,因此您应该使用 cursor + 1 - buffer 的大小来保持空分配块内的终止符。

这是修改后的版本:

#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;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res