• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            loop_in_codes

            低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

            記一次tcmalloc分配內(nèi)存引起的coredump

            現(xiàn)象

            線上的服務(wù)出現(xiàn)coredump,堆棧為:

            #0  0x000000000045d145 in GetStackTrace(void**, int, int) ()
            #1  0x000000000045ec22 in tcmalloc::PageHeap::GrowHeap(unsigned long) ()
            #2  0x000000000045eeb3 in tcmalloc::PageHeap::New(unsigned long) ()
            #3  0x0000000000459ee8 in tcmalloc::CentralFreeList::Populate() ()
            #4  0x000000000045a088 in tcmalloc::CentralFreeList::FetchFromSpansSafe() ()
            #5  0x000000000045a10a in tcmalloc::CentralFreeList::RemoveRange(void**, void**, int) ()
            #6  0x000000000045c282 in tcmalloc::ThreadCache::FetchFromCentralCache(unsigned long, unsigned long) ()
            #7  0x0000000000470766 in tc_malloc ()
            #8  0x00007f75532cd4c2 in __conhash_get_rbnode (node=0x22c86870, hash=30)
                    at build/release64/cm_sub/conhash/conhash_inter.c:88
            #9  0x00007f75532cd76e in __conhash_add_replicas (conhash=0x24fbc7e0, iden=<value optimized out>)
                    at build/release64/cm_sub/conhash/conhash_inter.c:45
            #10 0x00007f75532cd1fa in conhash_add_node (conhash=0x24fbc7e0, iden=0) at build/release64/cm_sub/conhash/conhash.c:72
            #11 0x00007f75532c651b in cm_sub::TopoCluster::initLBPolicyInfo (this=0x2593a400)
                    at build/release64/cm_sub/topo_cluster.cpp:114
            #12 0x00007f75532cad73 in cm_sub::TopoClusterManager::processClusterMapTable (this=0xa219e0, ref=0x267ea8c0)
                    at build/release64/cm_sub/topo_cluster_manager.cpp:396
            #13 0x00007f75532c5a93 in cm_sub::SubRespMsgProcess::reinitCluster (this=0x9c2f00, msg=0x4e738ed0)
                    at build/release64/cm_sub/sub_resp_msg_process.cpp:157
            ...
            

            查看了應(yīng)用層相關(guān)數(shù)據(jù)結(jié)構(gòu),基本數(shù)據(jù)都是沒(méi)有問(wèn)題的。所以最初懷疑是tcmalloc內(nèi)部維護(hù)了錯(cuò)誤的內(nèi)存,在分配內(nèi)存時(shí)出錯(cuò),這個(gè)堆棧只是問(wèn)題的表象。幾天后,線上的另一個(gè)服務(wù),基于同樣的庫(kù),也core了,堆棧還是一樣的。

            最初定位問(wèn)題都是從最近更新的東西入手,包括依賴的server環(huán)境,但都沒(méi)有明顯的問(wèn)題,所以最后只能從core的直接原因入手。

            分析GetStackTrace

            確認(rèn)core的詳細(xì)位置:

            # core在該指令
            0x000000000045d145 <_Z13GetStackTracePPvii+21>: mov    0x8(%rax),%r9
            
            (gdb) p/x $rip              # core 的指令位置
            $9 = 0x45d145
            (gdb) p/x $rax              
            $10 = 0x4e73aa58
            (gdb) x/1a $rax+0x8         # rax + 8 = 0x4e73aa60
            0x4e73aa60:     0x0
            

            該指令嘗試從[0x4e73aa60]處讀取內(nèi)容,然后出錯(cuò),這個(gè)內(nèi)存單元不可讀。但是具體這個(gè)指令在代碼中是什么意思,需要將這個(gè)指令對(duì)應(yīng)到代碼中。獲取tcmalloc的源碼,發(fā)現(xiàn)GetStackTrace根據(jù)編譯選項(xiàng)有很多實(shí)現(xiàn),所以這里選擇最可能的實(shí)現(xiàn),然后對(duì)比匯編以確認(rèn)代碼是否匹配。最初選擇的是stacktrace_x86-64-inl.h,后來(lái)發(fā)現(xiàn)完全不匹配,又選擇了stacktrace_x86-inl.h。這個(gè)實(shí)現(xiàn)版本里也有對(duì)64位平臺(tái)的支持。

            stacktrace_x86-inl.h里使用了一些宏來(lái)生成函數(shù)名和參數(shù),精簡(jiǎn)后代碼大概為:

            int GET_STACK_TRACE_OR_FRAMES {
                  void **sp;
                  unsigned long rbp;
                  __asm__ volatile ("mov %%rbp, %0" : "=r" (rbp));
                  sp = (void **) rbp;
            
                  int n = 0;
                  while (sp && n < max_depth) {
                    if (*(sp+1) == reinterpret_cast<void *>(0)) {
                      break;
                    }
                    void **next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
                    if (skip_count > 0) {
                      skip_count--;
                    } else {
                      result[n] = *(sp+1);
                      n++;
                    }
                    sp = next_sp;
                  }
                  return n;
                }

            NextStackFrame是一個(gè)模板函數(shù),包含一大堆代碼,精簡(jiǎn)后非常簡(jiǎn)單:

            template<bool STRICT_UNWINDING, bool WITH_CONTEXT>
                static void **NextStackFrame(void **old_sp, const void *uc) {
                  void **new_sp = (void **) *old_sp;
                  if (STRICT_UNWINDING) {
                    if (new_sp <= old_sp) return NULL;
                    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL;
                  } else {
                    if (new_sp == old_sp) return NULL;
                    if ((new_sp > old_sp)
                        && ((uintptr_t)new_sp - (uintptr_t)old_sp > 1000000)) return NULL;
                  }
                  if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL;
            
                  return new_sp;
                }

            上面這個(gè)代碼到匯編的對(duì)比過(guò)程還是花了些時(shí)間,其中匯編中出現(xiàn)的一些常量可以大大縮短對(duì)比時(shí)間,例如上面出現(xiàn)了100000,匯編中就有:

            0x000000000045d176 <_Z13GetStackTracePPvii+70>: cmp    $0x186a0,%rbx  # 100000=0x186a0
            

            注意NextStackFrame中的 if (STRICT_UNWINDING)使用的是模板參數(shù),這導(dǎo)致生成的代碼中根本沒(méi)有else部分,也就沒(méi)有1000000這個(gè)常量

            在對(duì)比代碼的過(guò)程中,可以知道關(guān)鍵的幾個(gè)寄存器、內(nèi)存位置對(duì)應(yīng)到代碼中的變量,從而可以還原core時(shí)的現(xiàn)場(chǎng)環(huán)境。分析過(guò)程中不一定要從第一行匯編讀,可以從較明顯的位置讀,從而還原整個(gè)代碼,函數(shù)返回指令、跳轉(zhuǎn)指令、比較指令、讀內(nèi)存指令、參數(shù)寄存器等都是比較明顯對(duì)應(yīng)的地方。

            另外注意GetStackTraceRecordGrowth中調(diào)用,傳入了3個(gè)參數(shù):

            GetStackTrace(t->stack, kMaxStackDepth-1, 3); // kMaxStackDepth = 31
            

            以下是我分析的簡(jiǎn)單注解:

            (gdb) disassemble
            Dump of assembler code for function _Z13GetStackTracePPvii:
            0x000000000045d130 <_Z13GetStackTracePPvii+0>:  push   %rbp
            0x000000000045d131 <_Z13GetStackTracePPvii+1>:  mov    %rsp,%rbp
            0x000000000045d134 <_Z13GetStackTracePPvii+4>:  push   %rbx
            0x000000000045d135 <_Z13GetStackTracePPvii+5>:  mov    %rbp,%rax
            0x000000000045d138 <_Z13GetStackTracePPvii+8>:  xor    %r8d,%r8d
            0x000000000045d13b <_Z13GetStackTracePPvii+11>: test   %rax,%rax
            0x000000000045d13e <_Z13GetStackTracePPvii+14>: je     0x45d167 <_Z13GetStackTracePPvii+55>
            0x000000000045d140 <_Z13GetStackTracePPvii+16>: cmp    %esi,%r8d        # while ( .. max_depth > n ?
            0x000000000045d143 <_Z13GetStackTracePPvii+19>: jge    0x45d167 <_Z13GetStackTracePPvii+55>
            0x000000000045d145 <_Z13GetStackTracePPvii+21>: mov    0x8(%rax),%r9    # 關(guān)鍵位置:*(sp+1) -> r9, rax 對(duì)應(yīng) sp變量
            0x000000000045d149 <_Z13GetStackTracePPvii+25>: test   %r9,%r9          # *(sp+1) == 0 ?
            0x000000000045d14c <_Z13GetStackTracePPvii+28>: je     0x45d167 <_Z13GetStackTracePPvii+55>
            0x000000000045d14e <_Z13GetStackTracePPvii+30>: mov    (%rax),%rcx      # new_sp = *old_sp,這里已經(jīng)是NextStackFrame的代碼
            0x000000000045d151 <_Z13GetStackTracePPvii+33>: cmp    %rcx,%rax        # new_sp <= old_sp ? 
            0x000000000045d154 <_Z13GetStackTracePPvii+36>: jb     0x45d170 <_Z13GetStackTracePPvii+64>  # new_sp > old_sp 跳轉(zhuǎn)
            0x000000000045d156 <_Z13GetStackTracePPvii+38>: xor    %ecx,%ecx
            0x000000000045d158 <_Z13GetStackTracePPvii+40>: test   %edx,%edx        # skip_count > 0 ?
            0x000000000045d15a <_Z13GetStackTracePPvii+42>: jle    0x45d186 <_Z13GetStackTracePPvii+86>
            0x000000000045d15c <_Z13GetStackTracePPvii+44>: sub    $0x1,%edx        # skip_count--
            0x000000000045d15f <_Z13GetStackTracePPvii+47>: mov    %rcx,%rax        
            0x000000000045d162 <_Z13GetStackTracePPvii+50>: test   %rax,%rax        # while (sp ?
            0x000000000045d165 <_Z13GetStackTracePPvii+53>: jne    0x45d140 <_Z13GetStackTracePPvii+16>
            0x000000000045d167 <_Z13GetStackTracePPvii+55>: pop    %rbx
            0x000000000045d168 <_Z13GetStackTracePPvii+56>: leaveq 
            0x000000000045d169 <_Z13GetStackTracePPvii+57>: mov    %r8d,%eax        # r8 存儲(chǔ)了返回值,r8=n
            0x000000000045d16c <_Z13GetStackTracePPvii+60>: retq                    # return n
            0x000000000045d16d <_Z13GetStackTracePPvii+61>: nopl   (%rax)
            0x000000000045d170 <_Z13GetStackTracePPvii+64>: mov    %rcx,%rbx        
            0x000000000045d173 <_Z13GetStackTracePPvii+67>: sub    %rax,%rbx        # offset = new_sp - old_sp
            0x000000000045d176 <_Z13GetStackTracePPvii+70>: cmp    $0x186a0,%rbx    # offset > 100000 ?
            0x000000000045d17d <_Z13GetStackTracePPvii+77>: ja     0x45d156 <_Z13GetStackTracePPvii+38> # return NULL
            0x000000000045d17f <_Z13GetStackTracePPvii+79>: test   $0x7,%cl         # new_sp & (sizeof(void*) - 1)
            0x000000000045d182 <_Z13GetStackTracePPvii+82>: je     0x45d158 <_Z13GetStackTracePPvii+40>
            0x000000000045d184 <_Z13GetStackTracePPvii+84>: jmp    0x45d156 <_Z13GetStackTracePPvii+38>
            0x000000000045d186 <_Z13GetStackTracePPvii+86>: movslq %r8d,%rax        # rax = n
            0x000000000045d189 <_Z13GetStackTracePPvii+89>: add    $0x1,%r8d        # n++
            0x000000000045d18d <_Z13GetStackTracePPvii+93>: mov    %r9,(%rdi,%rax,8)# 關(guān)鍵位置:result[n] = *(sp+1)
            0x000000000045d191 <_Z13GetStackTracePPvii+97>: jmp    0x45d15f <_Z13GetStackTracePPvii+47>
            

            分析過(guò)程比較耗時(shí),同時(shí)還可以分析下GetStackTrace函數(shù)的實(shí)現(xiàn)原理,其實(shí)就是利用RBP寄存器不斷回溯,從而得到整個(gè)調(diào)用堆棧各個(gè)函數(shù)的地址(嚴(yán)格來(lái)說(shuō)是返回地址)。簡(jiǎn)單示意下函數(shù)調(diào)用中RBP的情況:

               ...
            saved registers          # i.e push rbx
            local variabes           # i.e sub 0x10, rsp
            return address           # call xxx
            last func RBP            # push rbp; mov rsp, rbp
            saved registers
            local variables 
            return address
            last func RBP
            ...                      # rsp
            

            總之,一般情況下,任何一個(gè)函數(shù)中,RBP寄存器指向了當(dāng)前函數(shù)的棧基址,該棧基址中又存儲(chǔ)了調(diào)用者的棧基址,同時(shí)該棧基址前面還存儲(chǔ)了調(diào)用者的返回地址。所以,GetStackTrace的實(shí)現(xiàn),簡(jiǎn)單來(lái)說(shuō)大概就是:

            sp = rbp  // 取得當(dāng)前函數(shù)GetStackTrace的棧基址
                while (n < max_depth) {
                    new_sp = *sp
                    result[n] = *(new_sp+1)
                    n++
                }

            以上,最終就知道了以下關(guān)鍵信息:

            • r8 對(duì)應(yīng)變量 n,表示當(dāng)前取到第幾個(gè)棧幀了
            • rax 對(duì)應(yīng)變量 sp,代碼core在 *(sp+1)
            • rdi 對(duì)應(yīng)變量 result,用于存儲(chǔ)取得的各個(gè)地址

            然后可以看看現(xiàn)場(chǎng)是怎樣的:

            (gdb) x/10a $rdi
            0x1ffc9b98:     0x45a088 <_ZN8tcmalloc15CentralFreeList18FetchFromSpansSafeEv+40>       0x45a10a <_ZN8tcmalloc15CentralFreeList11RemoveRangeEPPvS2_i+106>
            0x1ffc9ba8:     0x45c282 <_ZN8tcmalloc11ThreadCache21FetchFromCentralCacheEmm+114>      0x470766 <tc_malloc+790>
            0x1ffc9bb8:     0x7f75532cd4c2 <__conhash_get_rbnode+34>        0x0
            0x1ffc9bc8:     0x0     0x0
            0x1ffc9bd8:     0x0     0x0
            
            (gdb) p/x $r8
            $3 = 0x5
            
            (gdb) p/x $rax
            $4 = 0x4e73aa58
            

            小結(jié):

            GetStackTrace在取調(diào)用__conhash_get_rbnode的函數(shù)時(shí)出錯(cuò),取得了5個(gè)函數(shù)地址。當(dāng)前使用的RBP為0x4e73aa58

            錯(cuò)誤的RBP

            RBP也是從堆棧中取出來(lái)的,既然這個(gè)地址有問(wèn)題,首先想到的就是有代碼局部變量/數(shù)組寫(xiě)越界。例如sprintf的使用。而且,一般寫(xiě)越界破壞堆棧,都可能是把調(diào)用者的堆棧破壞了,例如:

            char s[32];
            memcpy(s, p, 1024);
            

            因?yàn)閷?xiě)入都是從低地址往高地址寫(xiě),而調(diào)用者的堆棧在高地址。當(dāng)然,也會(huì)遇到寫(xiě)壞調(diào)用者的調(diào)用者的堆棧,也就是跨棧幀越界寫(xiě),例如以前遇到的:

            len = vsnprintf(buf, sizeof(buf), fmt, wtf-long-string);
            buf[len] = 0;
            

            __conhash_get_rbnode的RBP是在tcmalloc的堆棧中取的:

            (gdb) f 7
            #7  0x0000000000470766 in tc_malloc ()
            (gdb) x/10a $rsp
            0x4e738b80:     0x4e73aa58      0x22c86870
            0x4e738b90:     0x4e738bd0      0x85
            0x4e738ba0:     0x4e73aa58      0x7f75532cd4c2 <__conhash_get_rbnode+34>   # 0x4e73aa58
            

            所以這里就會(huì)懷疑是tcmalloc這個(gè)函數(shù)里有把堆棧破壞,這個(gè)時(shí)候就是讀代碼,看看有沒(méi)有疑似危險(xiǎn)的地方,未果。這里就陷入了僵局,懷疑又遇到了跨棧幀破壞的情況,這個(gè)時(shí)候就只能__conhash_get_rbnode調(diào)用棧中周圍的函數(shù)翻翻,例如調(diào)用__conhash_get_rbnode的函數(shù)__conhash_add_replicas中恰好有字符串操作:

            void __conhash_add_replicas(conhash_t *conhash, int32_t iden)
                {
                    node_t* node = __conhash_create_node(iden, conhash->replica);
                    ...
                    char buf[buf_len]; // buf_len = 64
                    ...
                    snprintf(buf, buf_len, VIRT_NODE_HASH_FMT, node->iden, i);
                    uint32_t hash = conhash->cb_hashfunc(buf);
                    if(util_rbtree_search(&(conhash->vnode_tree), hash) == NULL)
                    {
                        util_rbtree_node_t* rbnode = __conhash_get_rbnode(node, hash);
                        ...

            這段代碼最終發(fā)現(xiàn)是沒(méi)有問(wèn)題的,這里又耗費(fèi)了不少時(shí)間。后來(lái)發(fā)現(xiàn)若干個(gè)函數(shù)里的RBP都有點(diǎn)奇怪,這個(gè)調(diào)用棧比較正常的范圍是:0x4e738c90

            (gdb) f 8
            #8  0x00007f75532cd4c2 in __conhash_get_rbnode (node=0x22c86870, hash=30)
            (gdb) p/x $rbp
            $6 = 0x4e73aa58     # 這個(gè)還不算特別可疑
            (gdb) f 9
            #9  0x00007f75532cd76e in __conhash_add_replicas (conhash=0x24fbc7e0, iden=<value optimized out>)
            (gdb) p/x $rbp
            $7 = 0x4e738c60     # 這個(gè)也不算特別可疑
            (gdb) f 10
            #10 0x00007f75532cd1fa in conhash_add_node (conhash=0x24fbc7e0, iden=0) at build/release64/cm_sub/conhash/conhash.c:72
            (gdb) p/x $rbp      # 可疑
            $8 = 0x0
            (gdb) f 11
            #11 0x00007f75532c651b in cm_sub::TopoCluster::initLBPolicyInfo (this=0x2593a400)
            (gdb) p/x $rbp      # 可疑
            $9 = 0x2598fef0
            

            為什么很多函數(shù)中RBP都看起來(lái)不正常? 想了想真要是代碼里把堆棧破壞了,這錯(cuò)誤得發(fā)生得多巧妙?

            錯(cuò)誤RBP的來(lái)源

            然后轉(zhuǎn)機(jī)來(lái)了,腦海中突然閃出-fomit-frame-pointer。編譯器生成的代碼中是可以不需要棧基址指針的,也就是RBP寄存器不作為棧基址寄存器。大部分函數(shù)或者說(shuō)開(kāi)啟了frame-pointer的函數(shù),其函數(shù)頭都會(huì)有以下指令:

            push   %rbp
            mov    %rsp,%rbp
            ...
            

            表示保存調(diào)用者的棧基址到棧中,以及設(shè)置自己的棧基址。看下__conhash系列函數(shù);

            Dump of assembler code for function __conhash_get_rbnode:
            0x00007f75532cd4a0 <__conhash_get_rbnode+0>:    mov    %rbx,-0x18(%rsp)
            0x00007f75532cd4a5 <__conhash_get_rbnode+5>:    mov    %rbp,-0x10(%rsp)
            ...
            

            這個(gè)庫(kù)是單獨(dú)編譯的,沒(méi)有顯示指定-fno-omit-frame-pointer,查閱gcc手冊(cè),o2優(yōu)化是開(kāi)啟了omit-frame-pinter 的。

            在沒(méi)有RBP的情況下,tcmalloc的GetStackTrace嘗試讀RBP取獲取調(diào)用返回地址,自然是有問(wèn)題的。但是,如果整個(gè)調(diào)用棧中的函數(shù),要么有RBP,要么沒(méi)有RBP,那么GetStackTrace取出的結(jié)果最多就是跳過(guò)一些棧幀,不會(huì)出錯(cuò)。 除非,這中間的某個(gè)函數(shù)把RBP寄存器另作他用(編譯器省出這個(gè)寄存器肯定是要另作他用的)。所以這里繼續(xù)追查這個(gè)錯(cuò)誤地址0x4e73aa58的來(lái)源。

            來(lái)源已經(jīng)比較明顯,肯定是__conhash_get_rbnode中設(shè)置的,因?yàn)檫@個(gè)函數(shù)的RBP是在被調(diào)用者tcmalloc中保存的。

            Dump of assembler code for function __conhash_get_rbnode:
            0x00007f75532cd4a0 <__conhash_get_rbnode+0>:    mov    %rbx,-0x18(%rsp)
            0x00007f75532cd4a5 <__conhash_get_rbnode+5>:    mov    %rbp,-0x10(%rsp)
            0x00007f75532cd4aa <__conhash_get_rbnode+10>:   mov    %esi,%ebp                    # 改寫(xiě)了RBP
            0x00007f75532cd4ac <__conhash_get_rbnode+12>:   mov    %r12,-0x8(%rsp)
            0x00007f75532cd4b1 <__conhash_get_rbnode+17>:   sub    $0x18,%rsp
            0x00007f75532cd4b5 <__conhash_get_rbnode+21>:   mov    %rdi,%r12
            0x00007f75532cd4b8 <__conhash_get_rbnode+24>:   mov    $0x30,%edi
            0x00007f75532cd4bd <__conhash_get_rbnode+29>:   callq  0x7f75532b98c8 <malloc@plt>  # 調(diào)用tcmalloc,匯編到這里即可
            

            這里打印RSI寄存器的值可能會(huì)被誤導(dǎo),因?yàn)槿魏螘r(shí)候打印寄存器的值可能都是錯(cuò)的,除非它有被顯示保存。不過(guò)這里可以看出RSI的值來(lái)源于參數(shù)(RSI對(duì)應(yīng)第二個(gè)參數(shù)):

            void __conhash_add_replicas(conhash_t *conhash, int32_t iden)
                {
                    node_t* node = __conhash_create_node(iden, conhash->replica);
                    ...
                    char buf[buf_len]; // buf_len = 64
                    ...
                    snprintf(buf, buf_len, VIRT_NODE_HASH_FMT, node->iden, i);
                    uint32_t hash = conhash->cb_hashfunc(buf); // hash值由一個(gè)字符串哈希函數(shù)計(jì)算
                    if(util_rbtree_search(&(conhash->vnode_tree), hash) == NULL)
                    {
                        util_rbtree_node_t* rbnode = __conhash_get_rbnode(node, hash);  // hash值
                        ...

            追到__conhash_add_replicas

            0x00007f75532cd764 <__conhash_add_replicas+164>:        mov    %ebx,%esi    # 來(lái)源于rbx
            0x00007f75532cd766 <__conhash_add_replicas+166>:        mov    %r15,%rdi
            0x00007f75532cd769 <__conhash_add_replicas+169>:        callq  0x7f75532b9e48 <__conhash_get_rbnode@plt>
            
            (gdb) p/x $rbx
            $11 = 0x4e73aa58
            (gdb) p/x hash
            $12 = 0x4e73aa58      # 0x4e73aa58
            

            找到了0x4e73aa58的來(lái)源。這個(gè)地址值竟然是一個(gè)字符串哈希算法算出來(lái)的!這里還可以看看這個(gè)字符串的內(nèi)容:

            (gdb) x/1s $rsp
            0x4e738bd0:      "conhash-00000-00133"
            

            這個(gè)碉堡的哈希函數(shù)是conhash_hash_def

            coredump的條件

            以上,既然只要某個(gè)庫(kù)omit-frame-pointer,那tcmalloc就可能出錯(cuò),為什么發(fā)生的頻率并不高呢?這個(gè)可以回到GetStackTrace尤其是NextStackFrame的實(shí)現(xiàn),其中包含了幾個(gè)合法RBP的判定:

            if (new_sp <= old_sp) return NULL;  // 上一個(gè)棧幀的RBP肯定比當(dāng)前的大
                    if ((uintptr_t)new_sp - (uintptr_t)old_sp > 100000) return NULL; // 指針值范圍還必須在100000內(nèi)
                    ...
                if ((uintptr_t)new_sp & (sizeof(void *) - 1)) return NULL; // 由于本身保存的是指針,所以還必須是sizeof(void*)的整數(shù)倍,對(duì)齊

            有了以上條件,才使得這個(gè)core幾率變得很低。

            總結(jié)

            最后,如果你很熟悉tcmalloc,整個(gè)問(wèn)題估計(jì)就被秒解了:tcmalloc INSTALL

            另外附上另一個(gè)有意思的東西。

            在分析__conhash_add_replicas時(shí),其內(nèi)定義了一個(gè)64字節(jié)的字符數(shù)組,查看其堆棧:

            (gdb) x/20a $rsp
            0x4e738bd0:     0x2d687361686e6f63      0x30302d3030303030          # 這些是字符串conhash-00000-00133
            0x4e738be0:     0x333331        0x0
            0x4e738bf0:     0x0     0x7f75532cd69e <__conhash_create_node+78>
            0x4e738c00:     0x24fbc7e0      0x4e738c60
            0x4e738c10:     0x24fbc7e0      0x7f75532cd6e3 <__conhash_add_replicas+35>
            0x4e738c20:     0x0     0x24fbc7e8
            0x4e738c30:     0x4e738c20      0x24fbc7e0
            0x4e738c40:     0x22324360      0x246632c0
            0x4e738c50:     0x0     0x0
            0x4e738c60:     0x0     0x7f75532cd1fa <conhash_add_node+74>
            

            最開(kāi)始我覺(jué)得buf占64字節(jié),也就是整個(gè)[0x4e738bd0, 0x4e738c10)內(nèi)存,但是這塊內(nèi)存里居然有函數(shù)地址,這一度使我懷疑這里有問(wèn)題。后來(lái)醒悟這些地址是定義buf前調(diào)用__conhash_create_node產(chǎn)生的,調(diào)用過(guò)程中寫(xiě)到堆棧里,調(diào)用完后棧指針改變,但并不需要清空棧中的內(nèi)容。

            posted on 2015-04-06 18:33 Kevin Lynx 閱讀(8119) 評(píng)論(2)  編輯 收藏 引用 所屬分類: c/c++

            評(píng)論

            # re: 記一次tcmalloc分配內(nèi)存引起的coredump[未登錄](méi) 2015-04-08 09:32 error

            精彩的分析,能不能給個(gè)簡(jiǎn)單的總結(jié),怎么規(guī)避或者解決這種問(wèn)題?  回復(fù)  更多評(píng)論   

            # re: 記一次tcmalloc分配內(nèi)存引起的coredump 2015-04-08 20:55 egmkang

            gcc -fstack-protector -fstack-protector-all  回復(fù)  更多評(píng)論   

            奇米综合四色77777久久| 久久这里有精品视频| 亚洲国产另类久久久精品黑人| 亚洲另类欧美综合久久图片区| 免费一级做a爰片久久毛片潮| 热99RE久久精品这里都是精品免费 | 国产精品9999久久久久| 精品久久久久久无码中文字幕| 蜜桃麻豆WWW久久囤产精品| 精品久久久久中文字幕日本| 国産精品久久久久久久| 99蜜桃臀久久久欧美精品网站 | 9191精品国产免费久久| 久久综合精品国产一区二区三区 | 伊人久久综合热线大杳蕉下载| 久久国产精品偷99| 99国产欧美久久久精品蜜芽| 亚洲七七久久精品中文国产| 国内精品久久九九国产精品| 欧美精品久久久久久久自慰| 无码任你躁久久久久久| 国产精品美女久久久久AV福利| 亚洲中文字幕久久精品无码喷水 | 亚洲精品无码久久久| 国产精品18久久久久久vr| 久久精品国产色蜜蜜麻豆| 久久免费观看视频| 久久久久国产一区二区三区| 精品久久国产一区二区三区香蕉| 国产亚洲精品美女久久久| 欧美丰满熟妇BBB久久久| 伊人久久综合无码成人网| 久久久久久久精品成人热色戒| 无码任你躁久久久久久老妇| 精品人妻伦一二三区久久| 亚洲嫩草影院久久精品| 久久综合狠狠综合久久激情 | 一本一道久久精品综合| 狠狠色综合久久久久尤物| 久久午夜综合久久| 一本久久a久久精品综合香蕉|