什么是堆缓冲区溢出?
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?
我的代码
#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?