溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

strcpy在centos6.x,gcc4.4.7版本上會(huì)有bug,自我移動(dòng)導(dǎo)致覆蓋錯(cuò)誤overlap

發(fā)布時(shí)間:2020-07-24 13:05:06 來(lái)源:網(wǎng)絡(luò) 閱讀:2990 作者:angel_64 欄目:編程語(yǔ)言

Gcc編譯時(shí)無(wú)優(yōu)化參數(shù),以前曾經(jīng)被-O坑過(guò)。

 

#include <stdio.h>
#include <string.h>
 
int main()
{
       char url[512];
       sprintf(url,"218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4");
       printf("%s\n",url);
       char*p = url;
 
       strcpy(p+15,p+22);
       printf("%s\n",url);
       return 0;
}

打印結(jié)果應(yīng)該如下

218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4

218.26.242.56/06d6168bf1a7294ae0e1c071171adcd48.mp4

 

但是在centos6.3系統(tǒng)下,gcc4.4.7,

打印結(jié)果會(huì)是

218.26.242.56/0/0/1023/6d6168bf1a7294ae0e1c071171adcd48.mp4

218.26.242.56/0/f1a7294a1a7294ae0e1c071171adcd48.mp4

 

目前實(shí)驗(yàn)redhat5.05.7centos7.2系統(tǒng)下都不會(huì)出現(xiàn)問(wèn)題,唯有6.x(試了6.0、6.3、6.7)gcc4.4.7會(huì)有問(wèn)題

 

下載4.4.7源碼

wget http://ftp.gnu.org/gnu/gcc/gcc-4.4.7/gcc-4.4.7.tar.bz2

 

代碼如下

extern void abort (void);
extern int inside_main;
 
char *
strcpy (char *d, const char *s)
{
  char *r = d;
#if defined __OPTIMIZE__ &&!defined __OPTIMIZE_SIZE__
  if (inside_main)
    abort ();
#endif
  while ((*d++ = *s++));
  return r;
}

理論上不應(yīng)該出現(xiàn)如此問(wèn)題

Centos6.xstrcpy源碼為匯編碼

char *strcpy(char *dest, const char *src)
{
        return __kernel_strcpy(dest, src);
}
static inline char *__kernel_strcpy(char*dest, const char *src)
{
        char *xdest = dest;
 
        asm volatile ("\n"
                  "1:    move.b     (%1)+,(%0)+\n"
                  "       jne    1b"
                  : "+a" (dest), "+a" (src)
                  : : "memory");
        return xdest;
}

同樣看不出有什么問(wèn)題。

 

將系統(tǒng)函數(shù)修改為自定義函數(shù),使用一樣的代碼,結(jié)果均為正確。

網(wǎng)絡(luò)上也找到過(guò)另外一種優(yōu)化版本的strcpy代碼,使用寄存器加速效果,在網(wǎng)上找到的號(hào)稱gcc的優(yōu)化代碼也是類似

char *  
strcpy (dest, src)  
     char *dest;  
     const char *src;  
{  
  register char c;  
  char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);  
  const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;  
  size_t n;  
  
  do  
    {  
      c = *s++;  
      s[off] = c;  
    }  
  while (c != '\0');    
  n = s - src;  
  (void) CHECK_BOUNDS_HIGH (src + n);  
  (void) CHECK_BOUNDS_HIGH (dest + n);  
  
  return dest;  
}


將之作為自定義函數(shù)使用后發(fā)現(xiàn)也沒(méi)有問(wèn)題。


繼續(xù)發(fā)現(xiàn)strncpy和sprintf也會(huì)遇到同樣的問(wèn)題。

采用memcpy就沒(méi)有問(wèn)題了

memcpy(p+11,p+18,strlen(p+18)+1);


看了下源碼,跟strcpy也沒(méi)什么區(qū)別

void *
memcpy (void *dest, const void *src, size_t len)
{
  char *d = dest;
  const char *s = src;
  while (len--)
    *d++ = *s++;
  return dest;
}


暫時(shí)不明白為什么strcpy、strncpy、sprintf在gcc4.4.7下,自我移動(dòng)會(huì)導(dǎo)致問(wèn)題。

以前曾經(jīng)在網(wǎng)上看見(jiàn)過(guò)strcpy的優(yōu)化函數(shù),在64位系統(tǒng)里,采用八字節(jié)長(zhǎng)×××來(lái)進(jìn)行復(fù)制,但是未在庫(kù)中見(jiàn)過(guò),只是作為優(yōu)化的自定義代碼推薦。

在這里例子中,如果我們將p+15改成p+16,就一切正常,把字符串總長(zhǎng)度縮小到p+32(即*(p+32)=0),那么也一切正常。錯(cuò)誤字段長(zhǎng)度8字節(jié),跟8都有關(guān)系,懷疑系統(tǒng)在什么地方做了優(yōu)化,但是實(shí)在搞不清是誰(shuí)在優(yōu)化,優(yōu)化后的代碼是什么樣子的。


所以建議如果要進(jìn)行字符串自我移動(dòng),不要使用str,使用mem函數(shù)。


--------------------

同事提供了一個(gè)帖子,說(shuō)的是內(nèi)存重疊的問(wèn)題

http://blog.csdn.net/stpeace/article/details/39456645


但是這個(gè)例子的作者其實(shí)沒(méi)有分析到點(diǎn)子上

char str []="123456789";
strcpy(str + 2, str);

本身在代碼邏輯上就是錯(cuò)的,從源碼就能看出來(lái),從前往后復(fù)制,會(huì)導(dǎo)致后面的內(nèi)存覆蓋。和我們這次遇到的不是一個(gè)情況。


按照源碼應(yīng)該結(jié)果是1212121212121212。。。。。。。。。。。一直到越界崩潰

但是實(shí)際結(jié)果是121234565678


在幾個(gè)機(jī)器上試了下

在gcc4.1.1上,是12121212121。。。。。。 崩潰

Gcc4.4.7 顯示121234565678

gcc4.8.5 顯示12123456789


應(yīng)該是在4.4.7上確實(shí)有優(yōu)化,但是4.8.5應(yīng)該是解決了,而且連這種邏輯異常的也一起支持了。

在網(wǎng)上找到了gcc4.7上strcpy的匯編bug,為什么是不是也有關(guān)系。

-----------------


網(wǎng)上查了一下,發(fā)現(xiàn)被誤導(dǎo)了,這里應(yīng)該研究libc.so的代碼,而不是看gcc的代碼


看了下libc的源碼,define了不少實(shí)現(xiàn),在不同設(shè)備上使用了不同優(yōu)化的匯編碼,應(yīng)該是使用8字節(jié)復(fù)制代替單個(gè)字符串復(fù)制,在libc2.12有bug,libc2.17上應(yīng)該是解決了。


查看他們的strcpy代碼

libc2.12.1上

/* Copy SRC to DEST.  */
char *
strcpy (dest, src)
     char *dest;
     const char *src;
{
  reg_char c;
  char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
  const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
  size_t n;
  do
    {
      c = *s++;
      s[off] = c;
    }
  while (c != '\0');
  n = s - src;
  (void) CHECK_BOUNDS_HIGH (src + n);
  (void) CHECK_BOUNDS_HIGH (dest + n);
  return dest;
}


libc2.17上

/* Copy SRC to DEST.  */
char *
strcpy (dest, src)
     char *dest;
     const char *src;
{
  char c;
  char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
  const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
  size_t n;
  do
    {
      c = *s++;
      s[off] = c;
    }
  while (c != '\0');
  n = s - src;
  (void) CHECK_BOUNDS_HIGH (src + n);
  (void) CHECK_BOUNDS_HIGH (dest + n);
  return dest;
}

發(fā)現(xiàn)2.17相比2.12沒(méi)什么改動(dòng),就是取消了寄存器(reg_char變成了char),在這個(gè)博客里面說(shuō)過(guò)寄存器的重要性,可以提高copy速度,不明白為什么2.17取消了寄存器。

http://blog.csdn.net/astrotycoon/article/details/8114786


繼續(xù)測(cè)試

發(fā)現(xiàn)在libc2.12上

(gdb) bt
#0  0x00000036a7532664 in __strcpy_ssse3 () from /lib64/libc.so.6
#1  0x0000000000400671 in main () at test.c:32

真正使用的是ssse3指令集下的strcpy.S實(shí)現(xiàn)


在glib2.17下

(gdb) bt
#0  __strcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:232
#1  0x0000000000400655 in main () at test.c:9

直接進(jìn)入.s匯編碼,連libc.so都沒(méi)有進(jìn)入,不過(guò)這個(gè)匯編函數(shù)strcpy-sse2-unaligned也是glib里面的


對(duì)匯編實(shí)在無(wú)力,完全無(wú)從下手。

不過(guò)看來(lái)所謂的c源碼對(duì)分析沒(méi)有什么太大的幫助還容易引起誤解,因?yàn)榈讓拥膸?kù)根本就不用c程序的源碼啊。


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI