繁体   English   中英

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 将其写入文件时,会生成堆缓冲区溢出。

===EDIT===看起来问题来自使用带有malloc'd var的fprintf,因为fprintf本身在引擎盖下使用malloc,所以原来的alloc丢失了,memory泄漏。

所以我在不使用 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 是什么,现在我知道了多么宝贵的工具!

==EDIT-2== 正如 EmployedRussian 所指出的,原始代码中的问题不是 fprintf,而是缺少终止 '\0',请查看下面的答案,它确实修复了我的原始代码:)

看起来问题来自使用带有 malloc'd var 的 fprintf,因为 fprintf 本身在引擎盖下使用 malloc,所以原来的 alloc 丢失了,memory 泄漏。

恐怕你在这里吸取了错误的教训。

虽然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 字符串),并且您给了它一个指向具有以下字节的终止字符串的指针:

a b c \n X

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

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

 if (fread(fileContents, fileSize, 1, fp) != 1){ ... handle error
 fileContents[fileSize] = '\0';  // NUL-terminate the string.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM