使用简单的 malloc() 时如何修复 libc 中止错误

Posted

技术标签:

【中文标题】使用简单的 malloc() 时如何修复 libc 中止错误【英文标题】:How do i fix libc abort error when using a simple malloc() 【发布时间】:2022-01-05 03:34:58 【问题描述】:

我正在尝试重写 printf 函数。为了快速总结我的方法,我创建了一个包含 va_list 和其他一些整数变量的结构,以包含有关每种格式将使用哪些标志的信息。在 ft_printf 函数中,我正在写每个字母,直到找到一个 % 符号。一旦找到,我应用 ft_analise_flags 函数来检查潜在的标志。在该函数内部,我应用了另一个(ft_analise_width),它将检查可选宽度,然后在此内部我还调用另一个函数来查找可选精度。只有在我最终尝试转换格式之后。

我还没有完成程序,但我正在做一些测试,我发现了这个错误。当我使用宽度参数时,我得到了一堆错误。 当我使用 vs 代码调试器时,调用堆栈显示一些 libc.so.6! __GI_raise()、__GI_abort()、__malloc_asssert 等错误。它还提示说:(无法打开'raise.c')。

我已经搜索过这种错误,但我倾向于陷入我不明白如何应用的答案。我认为问题出在执行 malloc 时的 ft_subtr 函数中,因为那是调试器给出错误的地方。其他可能出现错误的地方是当我释放标签时,虽然我不明白为什么会出现错误。也许是因为我释放了一个指向包含 va_list 变量的结构的指针...

我知道这是很多代码,但如果有人理解这一点或遇到类似的问题,我想知道您是否有任何提示可以帮助我继续前进

#include <unistd.h>
#include <stdio.h> // official printf
#include <stdarg.h>
#include <stdlib.h>

size_t ft_strlen(const char *s)

    size_t i;

    i = 0;
    while (s[i] != '\0')
    
        i++;
    
    return (i);


char *ft_substr(char const *s, int start, int len)

    char *ptr;
    int index;

    ptr = (char *)malloc((len + 1) * sizeof(char));
    if (!ptr)
        return (NULL);
    index = 0;
    while (index < len)
    
        ptr[index] = s[start + index];
        index++;
    
    ptr[index] = '\0';
    return (ptr);


int ft_atoi(const char *str)

    int number;
    int sign;

    sign = 1;
    number = 0;
    while (*str == ' ' || *str == '\f' || *str == '\n' || *str == '\r' || *str == '\t' || *str == '\v')
        str++;
    if (*str == '+' || *str == '-')
    
        if (*str == '-')
            sign *= -1;
        str++;
    
    while (*str > 47 && *str < 58)
    
        number = (number * 10) + (*str - 48);
        str++;
    
    return (number * sign);


typedef struct print_settings

    va_list args;
    int hash;
    int zero;
    int dash;
    int space;
    int plus;
    int width;
    int precision;
    int point;
    int t_length;
 frt_settings;

frt_settings *ft_initialise_tab(frt_settings *tab)

    tab->hash = 0;
    tab->zero = 0;
    tab->dash = 0;
    tab->space = 0;
    tab->plus = 0;
    tab->width = 0;
    tab->precision = 0;
    tab->point = 0;
    tab->t_length = 0;
    return (tab);


frt_settings *set_tab(frt_settings *tab)

    int i;

    i = tab->t_length;
    ft_initialise_tab(tab);
    tab->t_length = i;
    return (tab);


void ft_print_percent(frt_settings *tab, char percent)

    char a = percent;
    tab->t_length += write(1, &a, 1);
    set_tab(tab);


int ft_convert(frt_settings *tab, const char *format, int i)

    if (format[i] == '%')
        ft_print_percent(tab, format[i]);
    /*else if (format[i] == 'c')
        ft_print_char(tab);
    else if (format[i] == 's')
        ft_print_string(tab);
    else if (format[i] == 'p')
        ft_print_void(tab);
    else if (format[i] == 'd')
        ft_print_dec_num(tab);
    else if (format[i] == 'i')
        ft_print_int(tab);
    else if (format[i] == 'u')
        ft_print_unsigned_dec(tab);
    else if (format[i] == 'x')
        ft_print_num_hex_lower(tab);
    else if (format[i] == 'X')
        ft_print_num_hex_upper(tab);*/
    return (i);


int ft_analise_precision(frt_settings *tab, const char *format, int i)

    int j;
    char *precision;

    tab->point = 1;
    j = i;
    while (format[i] > 48 && format[i] < 58)
        i++;
    if (j != i)
    
        precision = ft_substr(format, j, i - j);
        tab->precision = ft_atoi(precision);
        free(precision);
    
    i = ft_convert(tab, format, i);
    return (i);


int ft_analise_width(frt_settings *tab, const char *format, int i)

    int j;
    char *width;

    j = i;
    if (format[i] > 48 && format[i] < 58)
        while (format[i] > 47 && format[i] < 58)
            i++;
    if (j != i)
    
        width = ft_substr(format, j, i - j);
        tab->width = ft_atoi(width);
        free(width);
    
    if (format[i] == '.')
        i = ft_analise_precision(tab, format, i + 1);
    else
        i = ft_convert(tab, format, i);
    return (i);


int is_flag(char letter)

    char conversions[5];
    int i;

    conversions[0] = '-';
    conversions[1] = '0';
    conversions[2] = '#';
    conversions[3] = ' ';
    conversions[4] = '+';
    i = 0;
    while (i < 5)
        if (conversions[i++] == letter)
            return (1);
    return (0);


int ft_analise_flags(frt_settings *tab, const char *format, int i)

    while (is_flag(format[i]))
    
        if (format[i] == '-')
            tab->dash = 1;
        if (format[i] == '0')
            tab->zero = 1;
        if (format[i] == '#')
            tab->hash = 1;
        if (format[i] == ' ')
            tab->space = 1;
        if (format[i] == '+')
            tab->plus = 1;
        i++;
    
    i = ft_analise_width(tab, format, i);
    return (i);


int ft_printf(const char *format, ...)

    frt_settings *tab;
    int i;
    int t_printed;

    tab = (frt_settings *)malloc(sizeof(tab));
    if (!tab)
        return (-1);
    ft_initialise_tab(tab);
    va_start(tab->args, format);
    i = -1;
    t_printed = 0;
    while (format[++i])
    
        if (format[i] == '%')
            i = ft_analise_flags(tab, format, i + 1);
        else
            t_printed += write(1, &format[i], 1);
    
    va_end(tab->args);
    t_printed += tab->t_length;
    free(tab);
    return (t_printed);


int main()

    ft_printf("%22%\n");

【问题讨论】:

【参考方案1】:

我认为问题在于执行 malloc 时的 ft_subtr 函数

没有。问题是您的程序误用 malloc(导致堆损坏)。

使用 Address Sanitizer 构建它会产生:

$ gcc -g -fsanitize=address p.c
$ ./a.out
=================================================================
==677491==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000028 at pc 0x5608ee09d565 bp 0x7ffc9b587f60 sp 0x7ffc9b587f58
WRITE of size 4 at 0x602000000028 thread T0
    #0 0x5608ee09d564 in ft_initialise_tab /tmp/p.c:75
    #1 0x5608ee09e442 in ft_printf /tmp/p.c:214
    #2 0x5608ee09e5fc in main /tmp/p.c:233
    #3 0x7f778eb31e49 in __libc_start_main ../csu/libc-start.c:314
    #4 0x5608ee09d129 in _start (/tmp/a.out+0x1129)

0x602000000028 is located 16 bytes to the right of 8-byte region [0x602000000010,0x602000000018)
allocated by thread T0 here:
    #0 0x7f778ed7d7cf in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:145
    #1 0x5608ee09e418 in ft_printf /tmp/p.c:211
    #2 0x5608ee09e5fc in main /tmp/p.c:233
    #3 0x7f778eb31e49 in __libc_start_main ../csu/libc-start.c:314

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/p.c:75 in ft_initialise_tab
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 00 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
==677491==ABORTING

具体来说,这一行有问题(分配 8 个字节而不是预期的sizeof(struct print_settings)):

tab = (frt_settings *)malloc(sizeof(tab));

应该是:

    tab = malloc(sizeof(*tab));

(不应该~永远不要在C中返回malloc。)

【讨论】:

非常感谢

以上是关于使用简单的 malloc() 时如何修复 libc 中止错误的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Wasm 中实现“malloc”

libc.so.6 误删后修复

如何使用 Malloc Debug 来检查本机内存泄漏?

每次加载libc地址都不相同

一次__libc_message的排查

如何将一个简单的应用程序(仅使用标准 libc)移植到 iPhone OS?