简体   繁体   English

valgrind对大小1的无效读取(realloc导致错误“释放后使用情况”)

[英]valgrind invalid read of size 1 (realloc makes errors “use-after-free condition”)

The function of my path_append() function is. 我的path_append()函数的功能是。

  • append given two strings and store them in one global string. 附加给定的两个字符串并将它们存储在一个全局字符串中。
  • Add '/' to the midle of two strings. 在两个字符串的中间添加'/'。
  • returns the appended strings first address. 返回附加字符串的第一个地址。

But we store every input in the same variable with null termination. 但是我们将每个输入存储在具有空终止符的同一变量中。

eg:- as input "/usr", "bin" is given. 例如:-作为输入“ / usr”,给出“ bin”。

then output sholud be ---> "/usr/bin" 然后输出sholud是--->“ / usr / bin”

In memory it should be like this. 在内存中应该是这样的。

+---+---+---+---+---+---+---+---+----+
| / | u | s | r | / | b | i | n | \0 |
+---+---+---+---+---+---+---+---+----+

let us say the previous call is function's first time call. 假设上一次调用是函数的首次调用。 If we call it second time giving arguments like "/etc", "sshd" the output should be "/etc/sshd". 如果我们第二次调用它,并提供“ / etc”,“ sshd”之类的参数,则输出应为“ / etc / sshd”。 In memory it should be like this. 在内存中应该是这样的。

+---+---+---+---+---+---+---+---+----+---+---+---+---+---+---+---+---+---+----+
| / | u | s | r | / | b | i | n | \0 | / | e | t | c | / | s | s | h | d | \0 |
+---+---+---+---+---+---+---+---+----+---+---+---+---+---+---+---+---+---+----+

ERROR 错误

If we call it more than one time it gives valgrind "invalid read size" error pointing the realloc function call in my program. 如果我们多次调用它,则会出现valgrind“无效的读取大小”错误,并指出程序中的realloc函数调用。

Here is my program. 这是我的程序。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char *p_app = NULL; /* This is global variable */

char *path_append(const char *p1, const char *p2)
{
    static int p_app_len = 0;
    int cur_len;    /*current len*/

    /* length of '/' + strlen(p1) + strlen(p2) + null charachter */
    cur_len = (strlen(p1) + strlen(p2) + 2);
    p_app_len += cur_len;

    if ((p_app_len - cur_len) == 0) /* Detect first time call */
        p_app = (char *)malloc(p_app_len);
    else
        p_app = (char *)realloc(p_app, (size_t)p_app_len);

    sprintf(p_app + p_app_len - cur_len, "%s/%s", p1, p2);

    return p_app + p_app_len - cur_len;
}

int main()
{
    char *x = path_append("/usr", "bin");
    char *y = path_append("/home", "minol");

    printf("%s\n", x);
    printf("%s\n", y);

    free(p_app); /* we do not free x or y */
    return 0;
}

Here is My error output 这是我的错误输出

==4139== Memcheck, a memory error detector
==4139== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==4139== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==4139== Command: ./test2
==4139== 
==4139== Invalid read of size 1
==4139==    at 0x4C2E0E2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4139==    by 0x4EA6E3B: puts (ioputs.c:36)
==4139==    by 0x4007C3: main (test2.c:42)
==4139==  Address 0x51fc040 is 0 bytes inside a block of size 9 free'd
==4139==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4139==    by 0x40071F: path_append (test2.c:30)
==4139==    by 0x4007B3: main (test2.c:40)
==4139== 
==4139== Invalid read of size 1
==4139==    at 0x4C2E0F4: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4139==    by 0x4EA6E3B: puts (ioputs.c:36)
==4139==    by 0x4007C3: main (test2.c:42)
==4139==  Address 0x51fc041 is 1 bytes inside a block of size 9 free'd
==4139==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4139==    by 0x40071F: path_append (test2.c:30)
==4139==    by 0x4007B3: main (test2.c:40)
==4139== 
==4139== Invalid read of size 1
==4139==    at 0x4EB26FE: _IO_default_xsputn (genops.c:480)
==4139==    by 0x4EB06E1: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1353)
==4139==    by 0x4EA6ED6: puts (ioputs.c:41)
==4139==    by 0x4007C3: main (test2.c:42)
==4139==  Address 0x51fc040 is 0 bytes inside a block of size 9 free'd
==4139==    at 0x4C2CE8E: realloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4139==    by 0x40071F: path_append (test2.c:30)
==4139==    by 0x4007B3: main (test2.c:40)
==4139== 
/usr/bin
/etc/sshd
==4139== 
==4139== HEAP SUMMARY:
==4139==     in use at exit: 0 bytes in 0 blocks
==4139==   total heap usage: 2 allocs, 2 frees, 28 bytes allocated
==4139== 
==4139== All heap blocks were freed -- no leaks are possible
==4139== 
==4139== For counts of detected and suppressed errors, rerun with: -v
==4139== ERROR SUMMARY: 17 errors from 3 contexts (suppressed: 0 from 0)

This is caused by use-after-free condition. 这是由免后使用条件引起的。

The first call to path_append() returns a pointer x to some memory which was allocated by malloc() . 首次调用path_append()返回指向由malloc()分配的某些内存的指针x

The second call to path_append() calls realloc() which return a new pointer making the pointer x in main invalid. path_append()的第二次调用将调用realloc() ,该x将返回一个新指针,使main中的指针x无效。

Here p_app is changed to point to a larger memory segment, but x is not updated with the new value and still points to old memory. 此处p_app更改为指向更大的内存段,但是x不会用新值更新,仍然指向旧内存。

p_app = (char *)realloc(p_app, (size_t)p_app_len);

This wouldn't happen if a global variable wasn't used. 如果不使用全局变量,则不会发生这种情况。 To fix the design flaw, use an array of char pointers and add strings to it and pass them to a function instead of using a global. 要修复设计缺陷,请使用char指针数组,并向其中添加字符串,然后将它们传递给函数,而不要使用全局函数。

x is invalid after realloc() x在realloc()之后无效

the line 线

    p_app = (char *)realloc(p_app, (size_t)p_app_len);

the sting in p_app is now on a new place, but x still points to the old location of this string. p_app中的字符串现在位于新位置,但是x仍指向该字符串的旧位置。

so 所以

printf("%s\n", x);

is invalid. 是无效的。 (but in this case, the data are still there, so it will print the right thing) (但在这种情况下,数据仍然存在,因此它将打印正确的内容)

Edit, why the first string is still in memory: 编辑,为什么第一个字符串仍在内存中:

realloc can work like this (i make it simple, no error check): realloc可以这样工作(我很简单,没有错误检查):

char *newPos = malloc(newSize);
memcpy(newPos,oldPos,oldSize);
free(oldPos);

But free() only set the location of oldPos as not used, it don't delete the content of oldPos. 但是free()仅将oldPos的位置设置为未使用,它不会删除oldPos的内容。

Memory before realloc call can be like (here is sizeof size_t and intptr_t = 1, to make it simple): 重新分配调用之前的内存可以像这样(为简单起见,这里的sizeof size_t和intptr_t = 1):

1  NULL  4  'f' 'o' 'o' '\0'  0    0   0   0   0    0   0  0  0  0  

and after 之后

7  NULL  4  'f' 'o' 'o' '\0' NULL  8  'f' 'o' 'o' '\0'  0  0  0  0  

The first number is a pointer to the first alloced block. 第一个数字是指向第一个已分配块的指针。 The second number before realloc points to the next alloced Block, there is no other alloced Block, so it is NULL. realloc之前的第二个数字指向下一个已分配的Block,没有其他已分配的Block,因此为NULL。

The next is the lenght of the Alloced Bytes, 4 for sthe string "Foo". 下一个是分配字节的长度,字符串“ Foo”为4。 After realloc, the first valid alloced block start at adress 7. The previous block is still there but it freed, and not valid anymore. 重新分配后,第一个有效分配的块从地址7开始。上一个块仍然存在,但已释放,不再有效。 The block given from realloc() start at 7. The data start at 9, on adress 7 is Stored, where the next valid block is and on byte 8 the lenght of the block. 从realloc()给定的块开始于7。数据从9开始,存储在地址7处,下一个有效块位于该位置,在字节8处该块的长度。

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

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