堆缓冲区溢出与 fprintf
heap-buffer-overflow with fprintf
我正在更新我的问题,非常抱歉以错误的方式提问。
现在我可以将我的问题提炼成一段独立的代码:
#include <stdio.h>
#include <stdlib.h>
static __inline__ char* fileRead(char* file){
FILE* fp;
long fileSize;
char* fileContents;
fp = fopen ( file , "rb" );
if(!fp){
perror(file);
exit(1);}
/* this block writes the size of the file in fileSize */
fseek( fp , 0L , SEEK_END);
fileSize = ftell( fp );
rewind( fp );
/* allocate memory for entire content */
fileContents = malloc(fileSize+1);
if(!fileContents){
fclose(fp);
fputs("memory alloc fails",stderr);
exit(1);}
/* copy the file into the buffer */
if(fread(fileContents, fileSize, 1, fp) != 1){
fclose(fp);
free(fileContents);
fputs("entire read fails",stderr);
exit(1);}
/* close the file */
fclose(fp);
return fileContents;}
int main (){
char* head10 = "";
char* fileName = "testhtml.html";
FILE* out = fopen(fileName, "w");
head10 = fileRead("head10.html");
printf("%s\n", head10);
out = fopen(fileName, "wb");
fprintf(out, "%s\n", head10);
fclose(out);
free(head10);
return 0;}
这里是 head10.html 文件。
我正在用 -fsanitize=address
编译它,但出现堆缓冲区溢出。
错误似乎是在 fprintf(out, "%s\n", head10);
行引起的。
head10
是唯一的 malloc 变量,所以这是有道理的。
我可以使用 printf 毫无问题地打印它,但是当我尝试使用 fprintf 将它写入文件时,会生成堆缓冲区溢出。
===编辑===
看起来问题出在将 fprintf 与 malloc'd var 一起使用,因为 fprintf 本身在后台使用 malloc,因此原始分配丢失,内存泄漏。
所以我在没有使用 malloc 的情况下重写了我的函数:
#define _POSIX_C_SOURCE 200809L /* for getline() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static __inline__ void fileReset(char* fileName){
FILE* out = fopen(fileName, "w");
fwrite("" , sizeof(char) , strlen("") , out );
fclose(out);}
static __inline__ void fileAppend(char* fileName, char* string){
FILE* out = fopen(fileName, "a"); /* using "a" to APPEND */
if(fwrite(string , sizeof(char) , strlen(string) , out ) != strlen(string)){
printf("==file write error\n");
exit(EXIT_FAILURE);}
fclose(out);}
static __inline__ void fileAppendFile(char* source, char* dest){
FILE* in = fopen(source, "r");
char *line = NULL;
size_t len = 0;
size_t read;
while ((read = getline(&line, &len, in)) != -1) {
fileAppend(dest, line);}
free(line);
fclose(in);}
int main (){
char* fileName = "testhtml.html";
char* theme = "dark";
fileReset(fileName);
fileAppendFile("head10.html", fileName);
fileAppend(fileName, theme);
return 0;}
非常感谢所有的帮助,在这里非常菜鸟,不知道 -lasan 是什么,现在我知道这是一个多么宝贵的工具!
==编辑-2==
正如 EmployedRussian 指出的那样,原始代码中的问题不是 fprintf,而是缺少终止符 '\0',请查看下面的答案,它确实修复了我的原始代码:)
Looks like the problem came from using fprintf with a malloc'd var, as fprintf itself uses malloc under the hood, so the original alloc gets lost, and memory leaks.
恐怕你在这里吸取了错误的教训。
虽然 fprintf
确实可以在幕后使用 malloc
,但您的问题与此无关 。
我创建了一个包含 abc\n
(4 个字符)的 head10.html
文件。 运行 生成该输入文件的程序:
==10173==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000015 at pc 0x7fb5db2c7054 bp 0x7ffd44e74de0 sp 0x7ffd44e74590
READ of size 6 at 0x602000000015 thread T0
#0 0x7fb5db2c7053 (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x4d053)
#1 0x5654101dd435 in main /tmp/foo.c:43
#2 0x7fb5db0dde0a in __libc_start_main ../csu/libc-start.c:308
#3 0x5654101dd199 in _start (/tmp/a.out+0x1199)
0x602000000015 is located 0 bytes to the right of 5-byte region [0x602000000010,0x602000000015)
allocated by thread T0 here:
#0 0x7fb5db381628 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107628)
#1 0x5654101dd2db in fileRead /tmp/foo.c:20
#2 0x5654101dd425 in main /tmp/foo.c:42
#3 0x7fb5db0dde0a in __libc_start_main ../csu/libc-start.c:308
所以问题是您分配了 5 个字节(如预期的那样),但是 fprintf
试图从该缓冲区读取第 6 个字符。
为什么要这么做?因为您使用的格式:%s
期望找到一个终止 NUL
字符(即它期望一个正确终止的 C 字符串),并且您给它一个指向 non[=43 的指针=] 结尾的字符串,包含以下字节:
a b c \n X
第五个字节包含什么值?它是未定义的(它来自 malloc
,并且没有写入任何值)。由于该值是 not NUL
,fprintf
尝试读取 next (第 6 个)字节,这就是地址Sanitizer 发出错误信号并中止您的程序。
正确的解决方法是 NUL
终止字符串,如下所示:
if (fread(fileContents, fileSize, 1, fp) != 1){ ... handle error
fileContents[fileSize] = '[=12=]'; // NUL-terminate the string.
我正在更新我的问题,非常抱歉以错误的方式提问。
现在我可以将我的问题提炼成一段独立的代码:
#include <stdio.h>
#include <stdlib.h>
static __inline__ char* fileRead(char* file){
FILE* fp;
long fileSize;
char* fileContents;
fp = fopen ( file , "rb" );
if(!fp){
perror(file);
exit(1);}
/* this block writes the size of the file in fileSize */
fseek( fp , 0L , SEEK_END);
fileSize = ftell( fp );
rewind( fp );
/* allocate memory for entire content */
fileContents = malloc(fileSize+1);
if(!fileContents){
fclose(fp);
fputs("memory alloc fails",stderr);
exit(1);}
/* copy the file into the buffer */
if(fread(fileContents, fileSize, 1, fp) != 1){
fclose(fp);
free(fileContents);
fputs("entire read fails",stderr);
exit(1);}
/* close the file */
fclose(fp);
return fileContents;}
int main (){
char* head10 = "";
char* fileName = "testhtml.html";
FILE* out = fopen(fileName, "w");
head10 = fileRead("head10.html");
printf("%s\n", head10);
out = fopen(fileName, "wb");
fprintf(out, "%s\n", head10);
fclose(out);
free(head10);
return 0;}
这里是 head10.html 文件。
我正在用 -fsanitize=address
编译它,但出现堆缓冲区溢出。
错误似乎是在 fprintf(out, "%s\n", head10);
行引起的。
head10
是唯一的 malloc 变量,所以这是有道理的。
我可以使用 printf 毫无问题地打印它,但是当我尝试使用 fprintf 将它写入文件时,会生成堆缓冲区溢出。
===编辑=== 看起来问题出在将 fprintf 与 malloc'd var 一起使用,因为 fprintf 本身在后台使用 malloc,因此原始分配丢失,内存泄漏。
所以我在没有使用 malloc 的情况下重写了我的函数:
#define _POSIX_C_SOURCE 200809L /* for getline() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static __inline__ void fileReset(char* fileName){
FILE* out = fopen(fileName, "w");
fwrite("" , sizeof(char) , strlen("") , out );
fclose(out);}
static __inline__ void fileAppend(char* fileName, char* string){
FILE* out = fopen(fileName, "a"); /* using "a" to APPEND */
if(fwrite(string , sizeof(char) , strlen(string) , out ) != strlen(string)){
printf("==file write error\n");
exit(EXIT_FAILURE);}
fclose(out);}
static __inline__ void fileAppendFile(char* source, char* dest){
FILE* in = fopen(source, "r");
char *line = NULL;
size_t len = 0;
size_t read;
while ((read = getline(&line, &len, in)) != -1) {
fileAppend(dest, line);}
free(line);
fclose(in);}
int main (){
char* fileName = "testhtml.html";
char* theme = "dark";
fileReset(fileName);
fileAppendFile("head10.html", fileName);
fileAppend(fileName, theme);
return 0;}
非常感谢所有的帮助,在这里非常菜鸟,不知道 -lasan 是什么,现在我知道这是一个多么宝贵的工具!
==编辑-2== 正如 EmployedRussian 指出的那样,原始代码中的问题不是 fprintf,而是缺少终止符 '\0',请查看下面的答案,它确实修复了我的原始代码:)
Looks like the problem came from using fprintf with a malloc'd var, as fprintf itself uses malloc under the hood, so the original alloc gets lost, and memory leaks.
恐怕你在这里吸取了错误的教训。
虽然 fprintf
确实可以在幕后使用 malloc
,但您的问题与此无关 。
我创建了一个包含 abc\n
(4 个字符)的 head10.html
文件。 运行 生成该输入文件的程序:
==10173==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000015 at pc 0x7fb5db2c7054 bp 0x7ffd44e74de0 sp 0x7ffd44e74590
READ of size 6 at 0x602000000015 thread T0
#0 0x7fb5db2c7053 (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x4d053)
#1 0x5654101dd435 in main /tmp/foo.c:43
#2 0x7fb5db0dde0a in __libc_start_main ../csu/libc-start.c:308
#3 0x5654101dd199 in _start (/tmp/a.out+0x1199)
0x602000000015 is located 0 bytes to the right of 5-byte region [0x602000000010,0x602000000015)
allocated by thread T0 here:
#0 0x7fb5db381628 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x107628)
#1 0x5654101dd2db in fileRead /tmp/foo.c:20
#2 0x5654101dd425 in main /tmp/foo.c:42
#3 0x7fb5db0dde0a in __libc_start_main ../csu/libc-start.c:308
所以问题是您分配了 5 个字节(如预期的那样),但是 fprintf
试图从该缓冲区读取第 6 个字符。
为什么要这么做?因为您使用的格式:%s
期望找到一个终止 NUL
字符(即它期望一个正确终止的 C 字符串),并且您给它一个指向 non[=43 的指针=] 结尾的字符串,包含以下字节:
a b c \n X
第五个字节包含什么值?它是未定义的(它来自 malloc
,并且没有写入任何值)。由于该值是 not NUL
,fprintf
尝试读取 next (第 6 个)字节,这就是地址Sanitizer 发出错误信号并中止您的程序。
正确的解决方法是 NUL
终止字符串,如下所示:
if (fread(fileContents, fileSize, 1, fp) != 1){ ... handle error
fileContents[fileSize] = '[=12=]'; // NUL-terminate the string.