带有fprintf的堆缓冲区溢出

问题描述 投票:-4回答:1

我正在更新我的问题,非常抱歉以错误的方式提出。

现在我可以将我的问题提炼成一个独立的代码:

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

我正在用-fsanitize=address进行编译,并且得到了堆缓冲区溢出。错误似乎是在行fprintf(out, "%s\n", head10);引起的。head10是唯一的malloc变量,所以这很有意义。

我可以使用printf进行打印而不会出现问题,但是当我尝试使用fprintf将其写入文件时,会生成堆缓冲区溢出。

===编辑===看起来问题出在将fprintf与带有malloc的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是什么,现在我知道这是一个非常宝贵的工具!

我正在更新我的问题,非常抱歉以错误的方式提出问题。现在,我可以将问题分解为一个独立的代码:#include #include ...

c heap-corruption address-sanitizer
1个回答
0
投票

看起来问题出在将fprintf与带有malloc的var一起使用,因为fprintf本身在后台使用malloc,因此原始分配丢失,并且内存泄漏。

恐怕您在这里学到了错误的教训。

虽然fprintf确实可以在后台使用malloc,但您的问题与anything无关。

我创建了一个包含head10.html(4个字符)的abc\n文件。使用产生的输入文件运行程序:

==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终止的字符串的指针,该指针具有以下字节:

a b c \n X

第五个字节包含什么值?它是未定义的(它来自malloc,并且没有写入任何值)。由于该值为not NUL,因此fprintf尝试读取next(第6个)字节,这就是Address Sanitizer发出错误信号并中止程序的时间。

正确的解决方法是NUL终止字符串,如下所示:

 if (fread(fileContents, fileSize, 1, fp) != 1){ ... handle error
 fileContents[fileSize] = '\0';  // NUL-terminate the string.
© www.soinside.com 2019 - 2024. All rights reserved.