什么是堆缓冲区溢出?

What is heap-buffer-overflow?

我的代码

#include "libft.h"

size_t  ft_count_words(const char *s, char c)
{
    size_t  i;
    size_t  count;
    size_t  ibool;

    i = 0;
    count = 0;
    ibool = 1;
    while (*s)
    {
        while (*s == c && *s)
            s++;
        while (*s != c && *s)
        {
            if (ibool == 1)
                count++;
            ibool = 0;
            s++;
        }
        ibool = 1;
    }
    return (count);
}

size_t  ft_splitlen(const char *str, char c)
{
    size_t  i;

    i = 0;
    while (str[i] != c && str[i])
        i++;
    return (i);
}

char    *ft_splitdup(const char *str, char c)
{
    char    *word;
    size_t  i;

    i = 0;
    if (!(word = (char *)malloc((ft_splitlen(str, c) + 1) * sizeof(char))))
        return (NULL);
    while (str[i] != c && str[i])
    {
        word[i] = str[i];
        i++;
    }
    word[i] = '[=10=]';
    return (word);
}

char    **ft_splitfree(char **base_split)
{
    size_t  i;

    i = 0;
    while (base_split[i])
    {
        free(base_split[i]);
        i++;
    }
    free(base_split);
    return (NULL);
}

char    **ft_split(const char *s, char c)
{
    char    **best_split;
    size_t  i;
    size_t  j;

    if (!s || (!s && !c))
        return (NULL);
    i = 0;
    j = 0;
    if (!(best_split = (char **)malloc(sizeof(char *) *\
        ft_count_words(s, c) + 1)))
        return (NULL);
    while (s[i])
    {
        while (s[i] == c && s[i])
            i++;
        while (s[i] != c && s[i])
        {
            if (!(best_split[j] = ft_splitdup(s + i, c)))
                return (ft_splitfree(best_split));
            i += ft_splitlen(s + i, c);
            j++;
        }
    }
    best_split[j] = NULL;
    return (best_split);
}

#include <stdio.h>

int                 main()
{
    char **test;
    test = ft_split("a b c d e", ' ');
    int i = 0;
    while (test[i])
    {
        printf("%d\t", i);
        printf("%s\n", test[i]);
        i++;
    }
}

和 ASAN 结果

=================================================================
==2643==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x604000000038 at pc 0x5650a051182c bp 0x7ffc19904370 sp 0x7ffc19904360
WRITE of size 8 at 0x604000000038 thread T0
    #0 0x5650a051182b in ft_split (/home/taemkim/42_cursus/libft/test+0x182b)
    #1 0x5650a0511856 in main (/home/taemkim/42_cursus/libft/test+0x1856)
    #2 0x7f85cd8670b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
    #3 0x5650a05111ed in _start (/home/taemkim/42_cursus/libft/test+0x11ed)

0x604000000039 is located 0 bytes to the right of 41-byte region [0x604000000010,0x604000000039)
allocated by thread T0 here:
    #0 0x7f85cdb3fbc8 in malloc (/lib/x86_64-linux-gnu/libasan.so.5+0x10dbc8)
    #1 0x5650a051162b in ft_split (/home/taemkim/42_cursus/libft/test+0x162b)
    #2 0x5650a0511856 in main (/home/taemkim/42_cursus/libft/test+0x1856)
    #3 0x7f85cd8670b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/taemkim/42_cursus/libft/test+0x182b) in ft_split
Shadow bytes around the buggy address:
  0x0c087fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c087fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c087fff8000: fa fa 00 00 00 00 00[01]fa fa fa fa fa fa fa fa
  0x0c087fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c087fff8050: 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
==2643==ABORTING

我似乎做了正确的内存分配。但结果是堆缓冲区溢出。

ex) 如果 ft_split("a b c d e", ' '),

best_split = (char **)malloc(sizeof(char *) * 5 + 1

best_split = [a, b, c, d, e, NULL]

我用空格 space 分隔每个字符,并添加 1 因为 space 表示 NULL。

我觉得支数好,尺码也好。

为什么会出现这样的结果? 我该如何修复此代码?

堆缓冲区溢出是指在堆上分配的数组外部进行访问(即使用 malloc()).

问题是 best_split 数组不够大。

malloc(sizeof(char *) * ft_count_words(s, c) + 1)

将指针的大小乘以字数,然后仅将 1 个字节添加到其中,而不是指针的大小。您需要将单词数加 1,以便为额外的空指针获得足够的内存。

malloc(sizeof(char *) * (ft_count_words(s, c) + 1))

P.S。参见 Do I cast the result of malloc?