2013-04-30 93 views
-1

我正在虚拟机中运行程序。我正在执行一个循环,在某个时候,我调用了strcat。在一个数字(这个数字在不同的执行之间改变)之后,我收到了分段错误。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倍以上的功能。

+2

我们可以看到ChangetoDnsNameFormat吗?它可能有帮助 – lucasg 2013-04-30 07:12:52

+0

在这些情况下通常会出错的地方在于你写过数据结构的末尾。前两次你很幸运,那个结构之后的内存仍然在你的地址空间中,但是在某些时候你写的超出了进程本身的地址空间,那就是当内核用SIGSEGV敲你时。 – izak 2013-04-30 07:19:55

+1

你为'host'分配了多少空间? – sapi 2013-04-30 07:21:05

回答

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

当调用strcat,所述source将被附加到destination串(的destination终止空字符将通过source第一个字符等所取代)。 destination必须有足够的分配空间来包含连接的字符串。还要注意的是,sourcedestination必须以空终止的字符串

关于你的代码

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

由于您使用的参数host存储连接字符串,则必须确保调用ChangetoDnsNameFormathostnull-terminated string并含有足够分配的内存来存储额外.前。

记住strcat((char*)host,".")等同于:

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

这使得需要足够大,空结尾的字符串比较明确。

回溯建议你要么调用ChangetoDnsNameFormat不分配您的结尾点所需的空间,或者你缺少终止空字符hostdns

写入未分配的内存位置是undefined behavior,因此它可能立即崩溃或不立即崩溃。如果它工作1000次并在第1001次时导致段错误,这并不令人惊讶

+0

更糟糕的是:“a.com”是*字符串常数*,根本不可写。 – wildplasser 2013-04-30 10:58:06

+0

@wildplasser是的,这不是最相关的例子 – zakinster 2013-04-30 11:45:45

+0

主机被定义为unsigned char [200]并且是scanf-ed,所以我想它没有问题,并且dns是一个无符号字符*赋值如下:dns =(unsigned char *)&buf [sizeof(struct DNS_HEADER)],其中buf是一个无符号字符[65536]。我没有看到内存问题。 – user2333227 2013-04-30 16:43:10

1

Strcat()追加到字符串的末尾。如果你一直调用它,而不检查是否有空间添加你正在添加的素材,那么你最终会跑掉字符串的末尾。在字符串末尾运行得足够远,并且您可能会到达进程地址空间的末尾;操作系统会向你发送一个SIGSEGV。

通过上面的gdb trace的外观,你正在添加“。”。反复发生,直到发生。你没有给我足够的代码来确定你到底是什么类型的编码错误。

1

如果您继续追加到字符串的末尾,您将超出缓冲区的大小,因此可能会导致段错误。

0

更重要的是,你似乎根本不需要strcat()。只要改变strlen((char*)host)strlen((char*)host) + 1(或计算一次,并存储在一个局部变量为C strlen需要遍历整个字符串),并更改如果条件来

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

对待像字符串的结尾点你现在不再需要追加。

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