-1

仮想マシンでプログラムを実行しています。ある時点で strcat を呼び出すループを実行しています。いくつかのループ (この数は異なる実行間で変化します) の後、セグメンテーション違反を受け取ります。

私はそれをデバッグしようとしました:

(gdb) backtrace

0  0x001a3d5d in strcat () from /lib/tls/i686/cmov/libc.so.6

1  0x080493f4 in ChangetoDnsNameFormat (dns=0xbffef313 "", 
    host=0xbffff3b8 "a.com", '.' <repeats 195 times>...) at my_dns.c:378

2  0x08048c96 in nreplacehost (
    host=0xbffff3b8 "a.com", '.' <repeats 195 times>..., query_type=1, 
    ip=0xbffff354 "3.3.3.3") at my_dns.c:179

3  0x080489a1 in main (argc=774778414, argv=0xbffff4d4) at my_dns.c:106

(gdb) frame 1

1  0x080493f4 in ChangetoDnsNameFormat (dns=0xbffef313 "", 
    host=0xbffff3b8 "a.com", '.' <repeats 195 times>...) at my_dns.c:378
378     strcat((char*)host,".");

(gdb) print host

6 = (unsigned char *) 0xbffff3b8 "a.com", '.' <repeats 195 times>...

任意のヒント?

これは私が strcat を呼び出す関数です

void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host) 
{
    int lock = 0 , i;
    strcat((char*)host,".");

    for(i = 0 ; i < strlen((char*)host) ; i++) 
    {
        if(host[i]=='.') 
        {
            *dns++ = i-lock;
            for(;lock<i;lock++) 
            {
                *dns++=host[lock];
            }
            lock++; //or lock=i+1;
        }
    }
    *dns++='\0';
}

この関数は 1000 回以上正常に呼び出されています。

4

5 に答える 5

2
char * strcat ( char * destination, const char * source );

を呼び出すstrcatと、sourceが文字列に追加されdestinationます ( の終端のヌル文字はdestinationの最初の文字に置き換えられますsource)。連結された文字列を格納するのにdestination十分なスペースが割り当てられている必要があります。また、 と の両方が null で終わる文字列sourcedestinationなければならないことにも注意してください。

コードについて

void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host) {
    strcat((char*)host,".")

パラメータhostを使用して連結された文字列を格納しているため、呼び出す前にChangetoDnsNameFormatそれhostnull で終わる文字列であり、extra を格納するのに十分なメモリが割り当てられていることを確認する必要があります.

strcat((char*)host,".")と同等であることに注意してください:

host[strlen((char*)host)] = '.';
host[strlen((char*)host)+1] = '\0';

そのため、ヌルで終わる十分な大きさの文字列が非常に明確に必要になります。

バックトレースは、末尾のドットに必要なスペースを割り当てずに呼び出しているか、またはChangetoDnsNameFormatの終端の null 文字が欠落していることを示唆しています。hostdns

割り当てられていないメモリ位置への書き込みは未定義の動作であるため、すぐにクラッシュする場合とそうでない場合があります。1000回動いて1001回目にセグメンテーションフォールトしてもおかしくない

于 2013-04-30T07:21:34.243 に答える
1

Strcat() は文字列の末尾に追加します。追加する素材の余地があるかどうかを確認せずに呼び出し続けると、最終的に文字列の端から外れます。文字列の末尾から十分離れたところまで実行すると、おそらくプロセスのアドレス空間の末尾に到達します。その後、オペレーティング システムから SIGSEGV が送信されます。

上記の gdb トレースを見ると、「.」が追加されています。こうなるまで繰り返します。どのような種類のコーディングエラーが原因でこの時点に至ったのかを判断するのに十分なコードを示していません.

于 2013-04-30T07:21:06.393 に答える
1

文字列の末尾に追加し続けると、バッファのサイズを超えて、セグメンテーション違反が発生する可能性があります。

于 2013-04-30T07:22:12.913 に答える
0
#include <stdio.h>
#include <string.h>

void my_change( char* dns, char* host);
void hexdump(char *src, size_t len);

  /* function which does exactly the same as
  ** ChangetoDnsNameFormat() but without strcat()
  ** or enormous amounts of strlen() calls
  ** the caller should take care that
  ** *) dns is at least one byte larger than host
  ** *) host is properly terminated.
  */
void my_change( char *dns, char *host)
{
unsigned char *dst = (unsigned char*) dns
        , *src = (unsigned char*) host
        , *tick;

for (tick=dst++;  *dst = *src++; dst++) {
        if (*dst == '.') { *tick = (dst-tick-1); tick = dst; }
        }
*tick = (dst-tick-1);
}

void hexdump(char *src, size_t len)
{
size_t idx;
for (idx =0; idx < len; idx++) {
        fprintf(stderr, " %2x", src[idx] % 0xff );
        }
fputc( '\n', stderr);
}


/* And test it ... */

int main (void)
{

char source[] = "www.stackoverflow.com";
char target[1+sizeof source] = "";

my_change( target, source);

printf("Source:%s\n", source);
hexdump(source, strlen(source) );

printf("Myname:%s\n", target);
hexdump(target, strlen(target) );

return 0;
}
于 2013-04-30T14:28:04.690 に答える
0

さらに重要なことに、あなたはまったく必要ないようですstrcat()。変更strlen((char*)host)するだけでstrlen((char*)host) + 1(または一度計算してローカル変数に格納します。Cstrlenは文字列全体を反復処理する必要があるため)、if 条件を次のように変更します。

if(host[i]=='.' || host[i]=='\0') 

文字列の末尾をドットのように扱うには、追加する必要がなくなりました。

于 2013-04-30T10:47:10.170 に答える