青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

loop_in_codes

低調做技術__歡迎移步我的獨立博客 codemaro.com 微博 kevinlynx

記一次tcmalloc分配內存引起的coredump

現象

線上的服務出現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
...

查看了應用層相關數據結構,基本數據都是沒有問題的。所以最初懷疑是tcmalloc內部維護了錯誤的內存,在分配內存時出錯,這個堆棧只是問題的表象。幾天后,線上的另一個服務,基于同樣的庫,也core了,堆棧還是一樣的。

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

分析GetStackTrace

確認core的詳細位置:

# 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]處讀取內容,然后出錯,這個內存單元不可讀。但是具體這個指令在代碼中是什么意思,需要將這個指令對應到代碼中。獲取tcmalloc的源碼,發現GetStackTrace根據編譯選項有很多實現,所以這里選擇最可能的實現,然后對比匯編以確認代碼是否匹配。最初選擇的是stacktrace_x86-64-inl.h,后來發現完全不匹配,又選擇了stacktrace_x86-inl.h。這個實現版本里也有對64位平臺的支持。

stacktrace_x86-inl.h里使用了一些宏來生成函數名和參數,精簡后代碼大概為:

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是一個模板函數,包含一大堆代碼,精簡后非常簡單:

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

上面這個代碼到匯編的對比過程還是花了些時間,其中匯編中出現的一些常量可以大大縮短對比時間,例如上面出現了100000,匯編中就有:

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

注意NextStackFrame中的 if (STRICT_UNWINDING)使用的是模板參數,這導致生成的代碼中根本沒有else部分,也就沒有1000000這個常量

在對比代碼的過程中,可以知道關鍵的幾個寄存器、內存位置對應到代碼中的變量,從而可以還原core時的現場環境。分析過程中不一定要從第一行匯編讀,可以從較明顯的位置讀,從而還原整個代碼,函數返回指令、跳轉指令、比較指令、讀內存指令、參數寄存器等都是比較明顯對應的地方。

另外注意GetStackTraceRecordGrowth中調用,傳入了3個參數:

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

以下是我分析的簡單注解:

(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    # 關鍵位置:*(sp+1) -> r9, rax 對應 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,這里已經是NextStackFrame的代碼
0x000000000045d151 <_Z13GetStackTracePPvii+33>: cmp    %rcx,%rax        # new_sp <= old_sp ? 
0x000000000045d154 <_Z13GetStackTracePPvii+36>: jb     0x45d170 <_Z13GetStackTracePPvii+64>  # new_sp > old_sp 跳轉
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 存儲了返回值,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)# 關鍵位置:result[n] = *(sp+1)
0x000000000045d191 <_Z13GetStackTracePPvii+97>: jmp    0x45d15f <_Z13GetStackTracePPvii+47>

分析過程比較耗時,同時還可以分析下GetStackTrace函數的實現原理,其實就是利用RBP寄存器不斷回溯,從而得到整個調用堆棧各個函數的地址(嚴格來說是返回地址)。簡單示意下函數調用中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

總之,一般情況下,任何一個函數中,RBP寄存器指向了當前函數的?;?,該?;分杏执鎯α苏{用者的棧基址,同時該?;非懊孢€存儲了調用者的返回地址。所以,GetStackTrace的實現,簡單來說大概就是:

sp = rbp  // 取得當前函數GetStackTrace的?;?/span>
    while (n < max_depth) {
        new_sp = *sp
        result[n] = *(new_sp+1)
        n++
    }

以上,最終就知道了以下關鍵信息:

  • r8 對應變量 n,表示當前取到第幾個棧幀了
  • rax 對應變量 sp,代碼core在 *(sp+1)
  • rdi 對應變量 result,用于存儲取得的各個地址

然后可以看看現場是怎樣的:

(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

小結:

GetStackTrace在取調用__conhash_get_rbnode的函數時出錯,取得了5個函數地址。當前使用的RBP為0x4e73aa58。

錯誤的RBP

RBP也是從堆棧中取出來的,既然這個地址有問題,首先想到的就是有代碼局部變量/數組寫越界。例如sprintf的使用。而且,一般寫越界破壞堆棧,都可能是把調用者的堆棧破壞了,例如:

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

因為寫入都是從低地址往高地址寫,而調用者的堆棧在高地址。當然,也會遇到寫壞調用者的調用者的堆棧,也就是跨棧幀越界寫,例如以前遇到的:

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

所以這里就會懷疑是tcmalloc這個函數里有把堆棧破壞,這個時候就是讀代碼,看看有沒有疑似危險的地方,未果。這里就陷入了僵局,懷疑又遇到了跨棧幀破壞的情況,這個時候就只能__conhash_get_rbnode調用棧中周圍的函數翻翻,例如調用__conhash_get_rbnode的函數__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);
            ...

這段代碼最終發現是沒有問題的,這里又耗費了不少時間。后來發現若干個函數里的RBP都有點奇怪,這個調用棧比較正常的范圍是:0x4e738c90

(gdb) f 8
#8  0x00007f75532cd4c2 in __conhash_get_rbnode (node=0x22c86870, hash=30)
(gdb) p/x $rbp
$6 = 0x4e73aa58     # 這個還不算特別可疑
(gdb) f 9
#9  0x00007f75532cd76e in __conhash_add_replicas (conhash=0x24fbc7e0, iden=<value optimized out>)
(gdb) p/x $rbp
$7 = 0x4e738c60     # 這個也不算特別可疑
(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

為什么很多函數中RBP都看起來不正常? 想了想真要是代碼里把堆棧破壞了,這錯誤得發生得多巧妙?

錯誤RBP的來源

然后轉機來了,腦海中突然閃出-fomit-frame-pointer。編譯器生成的代碼中是可以不需要棧基址指針的,也就是RBP寄存器不作為?;芳拇嫫鳌4蟛糠趾瘮祷蛘哒f開啟了frame-pointer的函數,其函數頭都會有以下指令:

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

表示保存調用者的?;返綏V?,以及設置自己的棧基址。看下__conhash系列函數;

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)
...

這個庫是單獨編譯的,沒有顯示指定-fno-omit-frame-pointer,查閱gcc手冊,o2優化是開啟了omit-frame-pinter 的。

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

來源已經比較明顯,肯定是__conhash_get_rbnode中設置的,因為這個函數的RBP是在被調用者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                    # 改寫了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>  # 調用tcmalloc,匯編到這里即可

這里打印RSI寄存器的值可能會被誤導,因為任何時候打印寄存器的值可能都是錯的,除非它有被顯示保存。不過這里可以看出RSI的值來源于參數(RSI對應第二個參數):

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值由一個字符串哈希函數計算
        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    # 來源于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的來源。這個地址值竟然是一個字符串哈希算法算出來的!這里還可以看看這個字符串的內容:

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

這個碉堡的哈希函數是conhash_hash_def

coredump的條件

以上,既然只要某個庫omit-frame-pointer,那tcmalloc就可能出錯,為什么發生的頻率并不高呢?這個可以回到GetStackTrace尤其是NextStackFrame的實現,其中包含了幾個合法RBP的判定:

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

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

總結

最后,如果你很熟悉tcmalloc,整個問題估計就被秒解了:tcmalloc INSTALL

另外附上另一個有意思的東西。

在分析__conhash_add_replicas時,其內定義了一個64字節的字符數組,查看其堆棧:

(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>

最開始我覺得buf占64字節,也就是整個[0x4e738bd0, 0x4e738c10)內存,但是這塊內存里居然有函數地址,這一度使我懷疑這里有問題。后來醒悟這些地址是定義buf前調用__conhash_create_node產生的,調用過程中寫到堆棧里,調用完后棧指針改變,但并不需要清空棧中的內容。

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

評論

# re: 記一次tcmalloc分配內存引起的coredump[未登錄] 2015-04-08 09:32 error

精彩的分析,能不能給個簡單的總結,怎么規避或者解決這種問題?  回復  更多評論   

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

gcc -fstack-protector -fstack-protector-all  回復  更多評論   

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲风情在线资源站| 狠狠狠色丁香婷婷综合久久五月 | 欧美亚洲视频一区二区| 欧美护士18xxxxhd| 黄色国产精品| 欧美一区二区久久久| 亚洲肉体裸体xxxx137| 欧美中文字幕在线| 国产精品久久91| 亚洲视频在线视频| 亚洲日本成人网| 欧美成年人视频网站欧美| 尤物九九久久国产精品的分类| 欧美一区二区女人| 亚洲欧美日韩国产另类专区| 国产精品私拍pans大尺度在线| 亚洲一区二区三区久久| 亚洲精品久久久久久久久| 欧美国产日本| 一区二区三区日韩欧美精品| 亚洲精品国产视频| 欧美大片免费观看| 99视频精品免费观看| 日韩视频在线永久播放| 国产精品国产精品| 欧美一区亚洲二区| 欧美一区二区三区在线观看| 黄色国产精品| 亚洲黄色成人| 欧美视频日韩视频在线观看| 在线视频一区二区| 亚洲图片欧美一区| 国产亚洲人成a一在线v站| 久久精品一区二区三区四区| 久久久久国产一区二区三区| 亚洲高清色综合| 亚洲人成毛片在线播放| 欧美色区777第一页| 亚洲人妖在线| 欧美www视频| 亚洲美女福利视频网站| 一本高清dvd不卡在线观看| 国产精品影视天天线| 久久一区二区三区av| 蘑菇福利视频一区播放| 亚洲午夜精品17c| 欧美一区二区免费| 亚洲精品欧美极品| 午夜精品视频在线观看| 亚洲国产mv| 亚洲天堂成人| 国模私拍视频一区| 亚洲日韩欧美一区二区在线| 国产区精品在线观看| 亚洲国产精品成人综合色在线婷婷| 欧美日韩亚洲高清| 久久综合伊人77777| 欧美日韩一区二区视频在线| 久久精品国产久精国产思思| 欧美大片专区| 久久精品国亚洲| 欧美极品在线观看| 韩国精品主播一区二区在线观看| 亚洲九九九在线观看| 国产亚洲福利一区| 亚洲精品欧美日韩专区| 国产亚洲欧美一区二区三区| 亚洲美女诱惑| 亚洲国产婷婷综合在线精品| 亚洲综合导航| 一区二区av| 久久综合成人精品亚洲另类欧美| 亚洲综合首页| 免费成人小视频| 久久久精品动漫| 国产精品成av人在线视午夜片 | 欧美一区二区三区视频| 99视频精品全部免费在线| 久久精品观看| 亚洲欧美国产va在线影院| 欧美成人日韩| 美女日韩在线中文字幕| 国产欧美日韩一区二区三区在线| 亚洲精品久久| 一道本一区二区| 欧美电影电视剧在线观看| 午夜影视日本亚洲欧洲精品| 欧美精品一区视频| 牛夜精品久久久久久久99黑人| 国产精品日韩欧美综合| 99re这里只有精品6| 亚洲精品美女久久7777777| 久久久久久久一区| 乱中年女人伦av一区二区| 国产欧美日韩视频一区二区| 亚洲视频一二三| 99亚洲一区二区| 欧美大片免费| 亚洲区欧美区| 99国内精品久久| 欧美精品日日鲁夜夜添| 亚洲黄色在线| 在线天堂一区av电影| 欧美人与性动交cc0o| 免费观看成人网| 国产日韩欧美日韩| 欧美一区2区视频在线观看| 久久精品日韩欧美| 国产一区二区三区四区hd| 欧美在线视频网站| 久久亚洲春色中文字幕| 国内精品久久久久久影视8| 欧美在线二区| 欧美不卡一卡二卡免费版| 亚洲日本在线观看| 欧美日韩视频第一区| av成人动漫| 久久激情中文| 亚洲国产精品久久91精品| 欧美激情区在线播放| 一区二区福利| 久久se精品一区二区| 激情小说另类小说亚洲欧美| 最新国产成人av网站网址麻豆| 亚洲视频第一页| 国产精品99免视看9| 欧美不卡在线视频| 在线午夜精品自拍| 国产精品日本精品| 久久精品国产精品亚洲| 亚洲电影免费在线观看| 亚洲图片在线观看| 国产一区自拍视频| 欧美激情区在线播放| 亚洲欧美一区在线| 欧美国产日韩xxxxx| 亚洲影院高清在线| 经典三级久久| 欧美日韩国产精品成人| 午夜欧美精品| 亚洲国产精品久久人人爱蜜臀| 亚洲一区在线免费| 在线观看精品一区| 欧美视频专区一二在线观看| 久久gogo国模裸体人体| 亚洲精品一区二区三区四区高清| 欧美一区国产一区| 在线观看日韩专区| 国产精品www网站| 老司机午夜精品视频| 亚洲午夜成aⅴ人片| 亚洲成人在线视频网站| 亚洲欧美大片| 最新国产乱人伦偷精品免费网站| 欧美国产精品劲爆| 欧美在线免费播放| 一本久久精品一区二区| 免费高清在线一区| 午夜免费日韩视频| 亚洲精品影视在线观看| 国产亚洲欧美激情| 国产精品福利在线观看| 欧美成人免费在线| 久久国内精品自在自线400部| 99视频在线观看一区三区| 亚洲第一网站免费视频| 久久久久国产一区二区三区| 亚洲欧美激情视频在线观看一区二区三区| 欧美精品七区| 久久天堂精品| 欧美中文字幕在线| 欧美亚洲在线视频| 亚洲先锋成人| 夜夜嗨av一区二区三区中文字幕| 免费成人在线观看视频| 久久成人亚洲| 国产中文一区二区三区| 国产精品久久久一本精品| 欧美伊人久久久久久午夜久久久久 | 乱人伦精品视频在线观看| 午夜精品美女自拍福到在线| 一区二区欧美国产| 亚洲国产视频a| 亚洲欧美日韩专区| 国产亚洲福利社区一区| 国产精品久久久久久久久久久久久久 | 亚洲精品久久久久久久久| 欧美黄网免费在线观看| 女同性一区二区三区人了人一| 久久精品亚洲精品| 久久久激情视频| 久久深夜福利免费观看| 久久在线免费观看| 久久视频一区| 免费观看亚洲视频大全| 欧美黄色成人网| 日韩视频永久免费观看| 一区二区三区视频在线观看| 亚洲一区二区三区视频播放| 亚洲国产成人91精品|