3

以下のコードでは、文字列で読み取られたすべての文字を返す改行文字scanfからすべてを読み取って改行するように再作成しようとしています。stdin

しかし、問題は、特にrealloc呼び出されたときにコードがメモリをリークすることです。

getsまた、使用するのが危険な機能である理由も知りたいです

test.c: warning: the 'gets' function is dangerous and should not be used.

私のコード:-

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

#define MAX_BUF_LEN 128
#define __cdecl

//0 - Success
//-1 - Error in input
//1 - Error
__cdecl int read_input(char *s,int len){

char c,*t;
int l,i=0;

if(s==NULL || len < 1)
    return -1;

while((c=getchar()) != '\n'){

    //check if sufficient memory
    //required + used > assigned 
    l=strlen(s);
    if(l + 2 > len){

        len += l + MAX_BUF_LEN;     //realloc max to avoid subsequent realloc as its costly!

        t = realloc(s,len);
        if(t!=NULL)
            s = t;
        else
            return 1;               //No space to store content
    }

    s[i++] = c;

}

s[i++] = '\0';      //Null terminate the Buffer
return 0;   
}

int main(int argc,char* argv[]){

int len = 5+1;
char *s = calloc(len,sizeof(char));

printf("Enter your name\n");

if(!read_input(s,len))
    printf("Hi %s\n",s);

free(s);

return 0;
}

ヴァルグリンド:

nimish:~/Desktop$ gcc -g -Wall test.c -o test
nimish~/Desktop$ clear && valgrind ./test  --leak-check=full

==3670== Memcheck, a memory error detector
==3670== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==3670== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==3670== Command: ./test --leak-check=full
==3670== 
Enter your name
Nimish Nicolus
==3670== Conditional jump or move depends on uninitialised value(s)
==3670==    at 0x4027029: strlen (mc_replace_strmem.c:282)
==3670==    by 0x804850E: read_input (test.c:23)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40831BF: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A93A8: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1317)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502c is 4 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A93BF: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1317)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502b is 3 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A9330: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1349)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== Invalid read of size 1
==3670==    at 0x40A933C: _IO_file_xsputn@@GLIBC_2.1 (fileops.c:1348)
==3670==    by 0x408346F: vfprintf (vfprintf.c:1623)
==3670==    by 0x40891BF: printf (printf.c:35)
==3670==    by 0x80485E6: main (test.c:52)
==3670==  Address 0x41a502a is 2 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
Hi Nimis
==3670== Invalid free() / delete / delete[]
==3670==    at 0x4025BF0: free (vg_replace_malloc.c:366)
==3670==    by 0x80485F2: main (test.c:54)
==3670==  Address 0x41a5028 is 0 bytes inside a block of size 6 free'd
==3670==    at 0x402695A: realloc (vg_replace_malloc.c:525)
==3670==    by 0x8048537: read_input (test.c:28)
==3670==    by 0x80485CD: main (test.c:51)
==3670== 
==3670== 
==3670== HEAP SUMMARY:
==3670==     in use at exit: 139 bytes in 1 blocks
==3670==   total heap usage: 2 allocs, 2 frees, 145 bytes allocated
==3670== 
==3670== LEAK SUMMARY:
==3670==    definitely lost: 139 bytes in 1 blocks
==3670==    indirectly lost: 0 bytes in 0 blocks
==3670==      possibly lost: 0 bytes in 0 blocks
==3670==    still reachable: 0 bytes in 0 blocks
==3670==         suppressed: 0 bytes in 0 blocks
==3670== Rerun with --leak-check=full to see details of leaked memory
==3670== 
==3670== For counts of detected and suppressed errors, rerun with: -v
==3670== Use --track-origins=yes to see where uninitialised values come from
==3670== ERROR SUMMARY: 23 errors from 7 contexts (suppressed: 11 from 6)

編集:- 以下に添付された新しいコードを参照してください

新しいコード:-

__cdecl int read_input(char **s,int len){

char c,*t;
int l,i=0;

if((*s)==NULL || len < 1)
    return -1;

while((c=getchar()) != '\n'){

    //check if sufficient memory
    //required + used > assigned 
    l=strlen((*s));
    if(l + 2 > len){

        len += l + MAX_BUF_LEN;     //realloc max to avoid subsequent realloc as its costly!

        t = realloc((*s),len);
        if(t!=NULL)
            (*s) = t;
        else
            return 1;               //No space to store content
    }

    *((*s)+i++) = c;

}

*((*s)+i) = '\0';       //Null terminate the Buffer
return 0;   
} 

ヴァルグリンド:

==4767== Memcheck, a memory error detector
==4767== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==4767== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==4767== Command: ./test --leak-check=full
==4767== 
Enter your name
Nimish Nicolus
==4767== Conditional jump or move depends on uninitialised value(s)
==4767==    at 0x4027029: strlen (mc_replace_strmem.c:282)
==4767==    by 0x8048516: read_input (test.c:23)
==4767==    by 0x80485DE: main (test.c:51)
==4767== 
Hi Nimish Nicolus
==4767== 
==4767== HEAP SUMMARY:
==4767==     in use at exit: 0 bytes in 0 blocks
==4767==   total heap usage: 2 allocs, 2 frees, 145 bytes allocated
==4767== 
==4767== All heap blocks were freed -- no leaks are possible
==4767== 
==4767== For counts of detected and suppressed errors, rerun with: -v
==4767== Use --track-origins=yes to see where uninitialised values come from
==4767== ERROR SUMMARY: 6 errors from 1 contexts (suppressed: 11 from 6)

それでも問題が発生します。

4

4 に答える 4

4

問題についてマニュアルに記載されている内容は次のとおりです。gets

gets() を使用しないでください。データを事前に知らずに gets() が読み取る文字数を知ることは不可能であり、gets() はバッファーの末尾を超えて文字を格納し続けるため、使用するのは非常に危険です。コンピューターのセキュリティを破るために使用されています。代わりに fgets() を使用してください。

リークの問題は、ポインタsが値渡しされるために発生します。そのため、再割り当て後の割り当てを含むすべての変更は、元のポインタではなくそのコピーに対して行われます。結果として、解放sすると、最初に割り当てられたメモリのみが解放され、realloc-ed メモリがリークされます。これを修正するread_inputには、次のように、ポインターをポインターにするように変更します。

int read_input(char **ps,int len)

に渡し&s、関数の本体の代わりにread_input使用します。(*ps)s

于 2012-05-21T15:54:30.640 に答える
3

割り当てられたバッファへのポインタを返さない。だから、free(s)あなたが期待することをしていません。realloc同じ場所に余分なスペースを提供することは保証されていません。そのため、バッファが移動する可能性があります。解決策は、ポインターからポインターへのポインターを渡すか、代わりにポインターを返し、intNULL を使用してエラーを示すことです。

PS - 他にも問題があり、これは非常に安全でないコードです。

于 2012-05-21T15:55:10.973 に答える
2

ループでは、これを使用します:

l=strlen(s);

しかしs、ループ内のどこでも null で終了していませんでした。その後、それを呼び出すとstrlen()、期待どおりの結果が得られず、メモリ リークの原因となる可能性があります。そのため、ループ内で文字列を null で終了するようにしてください。

gets割り当てられたスペースを超えて読み取ることができるため、危険です。

于 2012-05-21T15:55:14.963 に答える
2

gets() は、バッファが実際にどれだけ大きいかを把握していないため危険です。したがって、大きな入力行でのバッファ オーバーランを防ぐことはできません。悪者にとって、バッファ オーバーランは非常に一般的な攻撃ベクトルの 1 つです。代わりに stdin を最初のパラメーターとして fgets() を使用してください。これにより、バッファーの大きさを指定できます。

于 2012-05-21T15:57:33.683 に答える