Search code examples
cprintfmallocvariadic-functionslibc

How do i fix libc abort error when using a simple malloc()


I'm trying to rewrite the printf function. To quickly summarize my approach, I've created a struct that contains the va_list and some other integer variables to contain information about what flags will be used in each format. In the ft_printf function I'm writing each letter until i find a % sign. Once found, I aplie the ft_analise_flags function to check for potential flags. Inside that function I aplie another one (ft_analise_width) which will check for an optional width and then inside this i also call another function which will look for an optional precision. Only after i finaly trie to convert the format.

I haven't finished the program but i was doing some tests and I found this errors. When I use the width parameter i get a bunch of errors. When I use the vs code debugger the call stack shows some libc.so.6! errors like, __GI_raise(), __GI_abort(), __malloc_asssert and some others. It also prompts something saying: (unable to open 'raise.c').

I have searched about this kind of errror but I tend to fall in answers that I dont understand how to aplie. I think the issue is either in th ft_subtr function when doing malloc, because thats where the debugger gives the errors. Other places where the errors could be coming from is when I free the tab, althought I dont understand why that would give an error. Perhaps because I'm freing a pointer that points to a struct containing a va_list variable...

I know this is a lot of code but if anyone understands this or has had a similer issue I would like to know if you have any hint that could help me go foward

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

Solution

  • I think the issue is either in th ft_subtr function when doing malloc

    No. The issue is that your program misuses malloc (causing heap corruption).

    Building it with Address Sanitizer yields:

    $ 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
    

    Specifically, this line is buggy (allocates 8 bytes instead of intended sizeof(struct print_settings)):

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

    It should be:

        tab = malloc(sizeof(*tab));
    

    (One should ~never cast the return of malloc in C.)