你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

如何排查Linux内存泄漏问题?

[复制链接]
gaosmile 发布时间:2020-5-26 22:52
内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。
我们平时开发过程中不可避免的会遇到内存泄漏问题,你是如何排查的呢?估计你是使用下面这几个工具吧?
  • valgrind
  • mtrace
  • dmalloc
  • ccmalloc
  • memwatch
  • debug_new

    + h: l" N6 M3 |* i) K9 I* Y
这里程序喵向大家推荐新的一个排查内存泄漏的工具:AddressSanitizer(ASan),该工具为gcc自带,4.8以上版本都可以使用,支持Linux、OS、Android等多种平台,不止可以检测内存泄漏,它其实是一个内存错误检测工具,可以检测的问题有:
  • 内存泄漏
  • 堆栈和全局内存越界访问
  • free后继续使用
  • 局部内存被外层使用
  • Initialization order bugs(中文不知道怎么翻译才好,后面有代码举例,重要)
    & U$ L  B2 _3 N& D7 c8 w+ n$ |
使用方法直接看我下面的代码:
检测内存泄漏
内存泄漏代码:

  1.   S4 U" S& `$ E. x
  2. #include <stdlib.h>
    * A# u+ `* y( I5 Y5 g6 J
  3. 2 M( ?$ {2 G, V% z. j# ]; s
  4. void func1() { malloc(7); }
    * W7 D5 [3 y- Z( A# F' \( y, @- n
  5. " h9 ?$ o' _0 {9 ]* S% g4 e
  6. void func2() { malloc(5); }8 ~( |+ ~3 b% T' J6 D3 J

  7. / |# N" s8 q6 P# H: f
  8. int main() {4 w) r6 _& z' Y3 W6 \
  9.    func1();
    + Q$ ?0 l- L. Y8 ^  u& \
  10.    func2();
    0 R" s: ?$ P* I, K' ^9 R
  11.    return 0;* }% g3 W& @5 l" D+ D  }
  12. }
复制代码
4 V5 B  S+ e' y/ O
% p7 l( p' I/ c1 r) ?. g0 O  n
编译and输出:

  1. ( I7 m9 V8 H* W1 H7 T$ @9 N5 V
  2. g++ -fsanitize=address -g test_leak.cc && ./a.out
    2 a, g/ B( B: P

  3. ! D/ |9 f4 j5 z9 g; y
  4. =================================================================% I8 a' W+ P6 D0 F* W
  5. ==103==ERROR: LeakSanitizer: detected memory leaks
    % x  \# ^6 e$ L! z- K+ D- @, N- O
  6. ! R  Z# c* b# v& o5 Z
  7. Direct leak of 7 byte(s) in 1 object(s) allocated from:% a# v4 v9 a# H# K
  8.    #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)3 j# o, ^' O- u
  9.    #1 0x7f95b36007f7 in func1() /home/wangzhiqiang/test/test_leak.cc:3
    ) h# m- K2 E6 [1 \8 h
  10.    #2 0x7f95b3600814 in main /home/wangzhiqiang/test/test_leak.cc:8& l! c: z8 s& D" q
  11.    #3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
      ^; Q: r- T2 g2 [5 S! n

  12. + c# Z/ [; l- h
  13. Direct leak of 5 byte(s) in 1 object(s) allocated from:' \0 j' e3 i& F& I
  14.    #0 0x7f95b231eb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)" @' ?! t7 f0 B2 h( b8 [, _
  15.    #1 0x7f95b3600808 in func2() /home/wangzhiqiang/test/test_leak.cc:5' N* [1 A; @' Q$ p! _* j. a
  16.    #2 0x7f95b3600819 in main /home/wangzhiqiang/test/test_leak.cc:9, N2 N  ~  I# U! }! @5 w
  17.    #3 0x7f95b1e61b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    $ F3 t4 f7 n3 [! C  m3 U
  18. ) {" b  Q  s; o: c) e* |/ Q
  19. SUMMARY: AddressSanitizer: 12 byte(s) leaked in 2 allocation(s).
复制代码
& V3 v) C7 A! }( O1 v* h5 N/ d

: c. z! m% P% \. _
编译方式很简单,只需要添加-fsanitize=address -g就可以检测出具体产生内存泄漏的位置以及泄漏空间的大小。
检测堆栈内存越界访问
示例:
  1. ; Y' H4 ?3 k0 f! Z9 Y
  2. #include <iostream>
    8 x. l$ y* W  O  g# p5 J

  3. - A0 ~/ |' a  s3 C& c: X
  4. int main() {
    4 u4 l( ~7 F; q& o. p
  5.    int *array = new int[100];
    % E/ Z# p( P) k1 s
  6.    array[0] = 0;) n' f: T4 J- t( U
  7.    int res = array[100];  // out of bounds
    9 ]* [: r4 Q2 V2 S9 `6 `+ z3 p6 p
  8.    delete[] array;2 o  c' Z3 t* T0 {% z4 Z+ @
  9.    return res;
    / w, A* `. J9 O7 i% r
  10. }
复制代码
* ], g' J+ U4 L% o7 H! c
编译and输出:
  1. ' o+ U0 ^+ p8 J3 l, k6 K
  2. g++ -fsanitize=address -g test_leak.cc && ./a.out
    9 D" |' @9 ^4 x/ ~$ h; b' t
  3.    
    - @8 T* b: ]* }
  4. =================================================================, D* [( G. M( g4 i" Z
  5. ==110==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6140000001d0 at pc 0x7f0e06400d2e bp 0x7ffff5963f10 sp 0x7ffff5963f00: W& Y2 n8 p6 K: Z
  6. READ of size 4 at 0x6140000001d0 thread T0, N1 N% z/ l5 b4 l
  7.    #0 0x7f0e06400d2d in main /home/wangzhiqiang/test/test_leak.cc:60 [2 s; x$ H' H( G# p
  8.    #1 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)" u; E3 m2 B& K- X6 `% u3 @3 N
  9.    #2 0x7f0e06400bb9 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xbb9)
    9 J5 x) ~; h3 f
  10. 1 E( }. x# y8 A+ \8 h8 z
  11. 0x6140000001d0 is located 0 bytes to the right of 400-byte region [0x614000000040,0x6140000001d0)
    9 ?1 W) f: T) o6 ]1 ?
  12. allocated by thread T0 here:# `7 b: D  j+ T! Y0 S
  13.    #0 0x7f0e05120608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)! Z) |  `  p# Y- `/ y
  14.    #1 0x7f0e06400cab in main /home/wangzhiqiang/test/test_leak.cc:4; @/ R( e* z, n3 H/ x2 ^
  15.    #2 0x7f0e048d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96). a+ T" Q4 C3 h6 e- m) h

  16. , }0 p( u+ A0 m% Q. z
  17. SUMMARY: AddressSanitizer: heap-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in main  T- S! ^' n; m' F
  18. Shadow bytes around the buggy address:
    + H$ L" g9 `: V  J# w6 e1 b
  19. 0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 007 @& U0 n8 f3 W  S' `$ Z$ S
  20. 0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    9 j3 h; W, d2 l$ B0 f5 a8 [
  21. 0x0c287fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00, d4 t; K+ k9 ^8 ^4 }
  22. 0x0c287fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 001 s" ?6 S, D4 R* j0 b1 q
  23. 0x0c287fff8020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ! T8 h. F4 [, g2 k7 `5 O
  24. =>0x0c287fff8030: 00 00 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa
    ; G  v! E# ?$ Q1 \$ J1 V5 A
  25. 0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa' \# P/ M; ?+ |3 T# ]4 Z0 r
  26. 0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa8 f( ]8 y0 `( ^4 j
  27. 0x0c287fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa% C8 B4 }2 d$ Z$ B: ~" b( t. e
  28. 0x0c287fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa# L9 f; [" \- Z7 N! k* Y
  29. 0x0c287fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
      a5 t  R) Z1 }2 g8 ~# q
  30. Shadow byte legend (one shadow byte represents 8 application bytes):7 A! @( M( f8 |% m" j
  31. Addressable:           00
    % c7 n/ k1 ~; M# z
  32. Partially addressable: 01 02 03 04 05 06 07! k5 _" r- ?2 Z8 [9 p8 H
  33. Heap left redzone:       fa
    % e! O2 r  j" x7 y/ B
  34. Freed heap region:       fd
    6 q4 d6 e$ S5 X! Z; v" C
  35. Stack left redzone:      f1
    + c' t" k1 y' L$ f* V  D& o
  36. Stack mid redzone:       f2
    ( a& _. P; [$ u7 B
  37. Stack right redzone:     f3
    3 @' }' {7 U5 o# B1 n) M% K
  38. Stack after return:      f5
    " x: H, y7 t' o
  39. Stack use after scope:   f8
    " h( l% M+ E% e- q3 T
  40. Global redzone:          f9
    4 [5 v" s' u0 L2 N- w
  41. Global init order:       f6
    7 I2 C6 I$ ]9 Q1 y
  42. Poisoned by user:        f7
      I6 \+ l2 y# q: ^( ]
  43. Container overflow:      fc
    % r1 ~# |( q' Z3 t% Y
  44. Array cookie:            ac) d3 v4 o/ N$ D: l
  45. Intra object redzone:    bb
    * O8 T1 k( S  E" p' X  u/ a
  46. ASan internal:           fe
      h9 W, B' _, ]) o- f
  47. Left alloca redzone:     ca! P. A% o. n& Q4 g3 j
  48. Right alloca redzone:    cb
    7 k% V$ D8 [4 F- E! X# Q
  49. ==110==ABORTING
复制代码
: Y& I) S" U! |/ A/ {* M0 P
可以方便定位到堆栈内存越界访问的错误。
全局内存越界访问:
示例:

  1. + x" n5 k( f, Y8 P
  2. #include <iostream>' \: ]) A2 U8 S4 W3 r( `* e; s
  3.   ]0 D/ \4 j, Q, x  S, f' T
  4. int global_array[100] = {0};
    9 \; B. v( F3 A, K3 X( a

  5. 3 W. o  L  o5 B1 N  Z$ O
  6. int main() {
    / T, u1 x7 P6 p
  7.    int res = global_array[100];  // out of bounds2 v7 G6 I" \" m( B
  8.    return 0;
    * a3 {7 E* ?- f9 i6 v
  9. }
复制代码
9 d7 V( O" T9 I
编译and输出:

  1.   K. F7 T' \9 H! _6 y  S
  2. g++ -fsanitize=address -g test_leak.cc && ./a.out: `- y9 K- }, I3 S: T" {
  3. =================================================================& }# g% q) ~9 ?' l& b+ B
  4. ==116==ERROR: AddressSanitizer: global-buffer-overflow on address 0x7f42e6e02310 at pc 0x7f42e6c00c84 bp 0x7fffdda52780 sp 0x7fffdda527701 {  L2 d. \$ s, K$ u
  5. READ of size 4 at 0x7f42e6e02310 thread T07 W3 _* v6 D3 F
  6.    #0 0x7f42e6c00c83 in main /home/wangzhiqiang/test/test_leak.cc:6) ?% I7 D# }( l5 B; ~, i# e
  7.    #1 0x7f42e50d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    & }0 R3 n) G. k  f0 ?5 m
  8.    #2 0x7f42e6c00b69 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb69)
    3 W# z: Z* B4 P1 ?5 e) O
  9. 8 V) I9 Q3 |& r8 b
  10. 0x7f42e6e02310 is located 0 bytes to the right of global variable 'global_array' defined in 'test_leak.cc:3:5' (0x7f42e6e02180) of size 400" ^. m5 U! ^2 ^/ R! _+ s7 }
  11. SUMMARY: AddressSanitizer: global-buffer-overflow /home/wangzhiqiang/test/test_leak.cc:6 in main
    ) g" @! W6 |* y9 g' o
  12. Shadow bytes around the buggy address:* K8 U% ^9 n2 r8 b/ u
  13. 0x0fe8dcdb8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00( x. X7 D4 k+ Q; ~
  14. 0x0fe8dcdb8420: 00 00 00 00 00 00 00 00 01 f9 f9 f9 f9 f9 f9 f99 @, P; v' _3 d8 f& K" m" J
  15. 0x0fe8dcdb8430: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00$ q$ X5 q% F! X: N2 k
  16. 0x0fe8dcdb8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ! n$ K0 k5 w! f
  17. 0x0fe8dcdb8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    * n* B+ j" x. Y) I( b$ K2 t
  18. =>0x0fe8dcdb8460: 00 00[f9]f9 f9 f9 f9 f9 00 00 00 00 00 00 00 00
      g$ N" `5 e2 j
  19. 0x0fe8dcdb8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    " {% U) w* t2 g! E
  20. 0x0fe8dcdb8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    " b8 T4 E' F6 [
  21. 0x0fe8dcdb8490: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ) y- V# R+ q# u% w! [# D
  22. 0x0fe8dcdb84a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00# X8 s# Z0 r" {  a' J
  23. 0x0fe8dcdb84b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  v* d3 @8 K/ N4 G! k, v3 ^6 [
  24. Shadow byte legend (one shadow byte represents 8 application bytes):
    ! ]# Z  X9 V# P! q
  25. Addressable:           00# {+ b( m/ C2 W% H1 S
  26. Partially addressable: 01 02 03 04 05 06 07
    9 |5 t# @1 h0 h' A, z' W
  27. Heap left redzone:       fa9 X3 h' }$ w% \4 A2 S
  28. Freed heap region:       fd5 H- N  }; `7 X; _& ]; H/ y% d
  29. Stack left redzone:      f1
    + E9 v) u/ G8 X/ A' x3 T; O5 m
  30. Stack mid redzone:       f2
    : z, p' F5 D2 e, ]
  31. Stack right redzone:     f3
    % ?+ \6 G1 q+ t6 ^4 h7 A
  32. Stack after return:      f5$ K. S7 _$ G3 E& z( r* m
  33. Stack use after scope:   f8
    ( }' @2 _7 g. t, U4 n3 v/ E: C
  34. Global redzone:          f9
    0 u1 r! F- I3 G) A5 X, c1 k! C% O4 a
  35. Global init order:       f6
    6 |; L  ~* o* y9 n: e6 K. f
  36. Poisoned by user:        f7
    5 A1 }  Y! P9 s( ]! O; D3 _
  37. Container overflow:      fc! A& i8 _+ ?. J7 f
  38. Array cookie:            ac
    + Y$ L4 b2 i' M
  39. Intra object redzone:    bb
    ; U' A& F4 d, f5 z
  40. ASan internal:           fe( y: w' H& G% M8 f! Y
  41. Left alloca redzone:     ca0 z7 p6 }: p' P, F/ o3 g/ ?
  42. Right alloca redzone:    cb
      W7 R. ~; K8 ~: Q# Q3 J
  43. ==116==ABORTING
复制代码
$ N5 E0 G" c: N* K6 [% ^
局部内存被外层使用
示例:
  1. 0 U8 c4 M6 W" w1 {& M
  2. #include <iostream>
    6 f; w& B8 W1 Y

  3. 0 }/ K! @; o* G: Z+ j
  4. volatile int *p = 0;
    , d$ I! Q0 c0 i8 E# @* s
  5. , J/ O; q' Q5 k
  6. int main() {7 e% k4 c: p# q3 E: J% @. y
  7. {4 i" g: d& S0 r, e% u" q& ^+ O
  8.    int x = 0;4 q1 V; B: C: M+ U; B
  9.    p = &x;
      l0 x6 m+ r& ^! i! P, W2 Y9 C# Q
  10. }# h0 `( j  }6 Q5 K( j
  11. *p = 5;; e+ Z  J' e/ j1 s; W. N# {6 J
  12. return 0;
    4 s9 \" {) Z2 Q6 Y$ n* N8 P) K0 B1 i  K
  13. }
复制代码

4 W6 {( f% d9 |) K* C! ?! j2 o

5 o' k/ P7 t: r. U) d& x. L
编译and输出:

  1. & r) x. d( t5 C" M: i/ S0 l
  2. g++ -fsanitize=address -g test_leak.cc && ./a.out! v% N& v& F8 O* _
  3. =================================================================
    # S. c* [$ q6 e/ f  J) @
  4. ==243==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fffce12a4b0 at pc 0x7f3993e00e7e bp 0x7fffce12a480 sp 0x7fffce12a470
      {" o. E6 L0 ?7 y: U( s
  5. WRITE of size 4 at 0x7fffce12a4b0 thread T0  q/ d4 Z" a. N
  6.   #0 0x7f3993e00e7d in main /home/wangzhiqiang/test/test_leak.cc:10
    + J* S8 N! ?! j' ~& l$ d; T; J
  7.   #1 0x7f39922d1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)! R- x8 @% c! `6 |7 U! M
  8.   #2 0x7f3993e00c89 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xc89)/ r: R. a# y; v
  9. ' C# t+ Y+ w( g
  10. Address 0x7fffce12a4b0 is located in stack of thread T0 at offset 32 in frame, s0 S9 u0 L" p: d% ]( s) K. j
  11.   #0 0x7f3993e00d79 in main /home/wangzhiqiang/test/test_leak.cc:5
    ! \5 i# \# N$ e  ~& _- d2 o8 B
  12. 8 E2 n; c! K! T2 L0 O) w
  13. This frame has 1 object(s):/ t6 c+ ]: b3 t& X2 E/ J* w
  14.   [32, 36) 'x' <== Memory access at offset 32 is inside this variable/ V% W" H/ w. q# @" _1 f
  15. HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
    7 Q. K3 f9 M' F8 N4 h& C; a
  16.     (longjmp and C++ exceptions *are* supported)6 v# L) O8 u0 _3 C9 z( F
  17. SUMMARY: AddressSanitizer: stack-use-after-scope /home/wangzhiqiang/test/test_leak.cc:10 in main& e# ?6 p0 \1 `6 t* p
  18. Shadow bytes around the buggy address:
    : j0 U% A5 _! K" q, d+ c- t
  19. 0x100079c1d440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 002 B2 v, O6 c' B. q
  20. 0x100079c1d450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    3 ~/ W1 l9 f7 w0 ^
  21. 0x100079c1d460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    + z  s: ~1 ~/ o& {- F) A
  22. 0x100079c1d470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00$ x4 O0 N5 z9 J9 D$ @" G
  23. 0x100079c1d480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    . V" S9 b" c: r" z
  24. =>0x100079c1d490: 00 00 f1 f1 f1 f1[f8]f2 f2 f2 00 00 00 00 00 00" |0 d  P. B; Q
  25. 0x100079c1d4a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    - i6 ?: l8 \6 ?" W1 C5 w$ d" f: ]
  26. 0x100079c1d4b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00! T! s3 ?8 p5 \! ~
  27. 0x100079c1d4c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00! w+ M- ?0 z/ K
  28. 0x100079c1d4d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  _6 j; e, ?1 B5 s7 t0 R, C$ U
  29. 0x100079c1d4e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 002 K% |/ a/ J# h. M& o& c; E
  30. Shadow byte legend (one shadow byte represents 8 application bytes):
    ; c$ S8 R- s% A3 ?! s, S
  31. Addressable:           00
    $ d) K# a1 a8 Y  z3 H/ {, F
  32. Partially addressable: 01 02 03 04 05 06 07
    ! P# q1 r- @, L5 ~
  33. Heap left redzone:       fa5 w: ?/ M6 \& q9 D
  34. Freed heap region:       fd
    # s1 [3 N" s: i. a
  35. Stack left redzone:     f1
    * M! g) W+ Q) a9 a! n: Y5 O% n! Q
  36. Stack mid redzone:       f2! T: {- u) v( z7 M' T( ~
  37. Stack right redzone:     f3) n  ]0 x& x2 Y# x
  38. Stack after return:     f5( N7 B7 G; r* E& k4 B
  39. Stack use after scope:   f8
      K+ r; Z$ S4 ?8 B  A' P# C
  40. Global redzone:         f96 P3 V9 T2 A; d' s9 B; h
  41. Global init order:       f6# j2 I" r, q% {4 ?9 h: f  L
  42. Poisoned by user:       f7. m/ @, T2 v8 u9 R! c: V. O
  43. Container overflow:     fc/ o7 O$ F6 `1 P, N
  44. Array cookie:           ac# K5 D+ V8 C2 }6 N& [% |0 a
  45. Intra object redzone:   bb# U' N: M9 N" Z
  46. ASan internal:           fe0 V8 F9 v4 ~! x4 ?/ _" J
  47. Left alloca redzone:     ca% T3 a5 _- w1 J
  48. Right alloca redzone:   cb4 l  r2 x& |, U; y9 P& G: G% C2 i
  49. ==243==ABORTING
复制代码
1 z0 G. C" ^  `2 c1 l! W
free后被使用
示例:

  1. 5 w/ f0 X# U; N3 b+ W% g6 o
  2. #include <iostream>
    - i; E7 j" O% F/ [" a4 Z/ q% q

  3. 3 ~8 l' ]: F* O+ x; u
  4. int main() {1 M" G2 K7 ?( N  x" P
  5.     int *array = new int[100];
    5 x! ~  k1 h8 Q0 S! K' x. z
  6.     delete[] array;$ t/ I1 v$ ?' H0 [! H
  7.     int a = array[0];  // error1 |* N8 n0 N! o( M  @6 s$ w) _
  8.     return 0;
    " l* j% Q. ], v4 @7 a
  9. }
复制代码
8 F3 \) y1 |/ E* Y9 s
编译and输出:
  1. - v4 p$ B9 ]6 Y" c# B3 {0 T! C
  2. g++ -fsanitize=address -g test_leak.cc && ./a.out9 D; C; |1 K6 e% K3 s
  3. =================================================================
    / l# i) A, v$ ?
  4. ==282==ERROR: AddressSanitizer: heap-use-after-free on address 0x614000000040 at pc 0x7f209fa00caa bp 0x7ffff2a15020 sp 0x7ffff2a15010
      X& E* I# K- W% R8 n
  5. READ of size 4 at 0x614000000040 thread T0  ~1 N" q/ f& P9 Q1 P
  6.     #0 0x7f209fa00ca9 in main /home/wangzhiqiang/test/test_leak.cc:6
    8 d: `- s( S3 |0 ~8 M  I
  7.     #1 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    / }5 v: ]/ p' d) p
  8.     #2 0x7f209fa00b69 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb69)
    - C$ v: Q& Q; I& U( D/ z

  9. % a# a. c% d$ G: W; i' o- Q
  10. 0x614000000040 is located 0 bytes inside of 400-byte region [0x614000000040,0x6140000001d0)
    ! d0 y/ F: B& N2 W; ~
  11. freed by thread T0 here:1 }) C) G% h" [0 c. R
  12.     #0 0x7f209e721480 in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe1480)
    , S- ^. Y  C: @6 M$ _
  13.     #1 0x7f209fa00c72 in main /home/wangzhiqiang/test/test_leak.cc:5, m  K6 @5 ?  q# @+ h4 _/ C, I; e
  14.     #2 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)5 N3 K' T/ G) f: t3 f2 @

  15. ' j7 A  b0 J6 @: P* L( ]
  16. previously allocated by thread T0 here:
    / L# |) b5 q% Q5 v: B* {
  17.     #0 0x7f209e720608 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0608)7 }# S! o! W* R4 M3 V
  18.     #1 0x7f209fa00c5b in main /home/wangzhiqiang/test/test_leak.cc:4
    2 z) A6 g: X" M  o
  19.     #2 0x7f209ded1b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    : |& q' a) k8 W- ]' O

  20. 9 k/ l3 o* Q" O
  21. SUMMARY: AddressSanitizer: heap-use-after-free /home/wangzhiqiang/test/test_leak.cc:6 in main  F6 R  j0 q3 b: E
  22. Shadow bytes around the buggy address:
    : [' X6 m# Q; u6 f* _
  23.   0x0c287fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    + D9 s$ B4 E$ m" f2 U; `' |' F% q
  24.   0x0c287fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ; J) x$ T* b' x  B5 u
  25.   0x0c287fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    : \6 B! q! [6 o
  26.   0x0c287fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" a+ P6 R  k/ F  X6 ?
  27.   0x0c287fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00& F) D1 a; v9 z: G) O) z3 L! F
  28. =>0x0c287fff8000: fa fa fa fa fa fa fa fa[fd]fd fd fd fd fd fd fd
    2 a- d: u' _& j% k
  29.   0x0c287fff8010: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd3 g+ c6 z; g  \+ a" F
  30.   0x0c287fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
    . I# _" r/ C1 T
  31.   0x0c287fff8030: fd fd fd fd fd fd fd fd fd fd fa fa fa fa fa fa: j/ e% Y( C1 b6 l
  32.   0x0c287fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa+ G! }6 z3 t% P. Y% g+ M1 r
  33.   0x0c287fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
    " U0 S) y. _' q* v% e" ]
  34. Shadow byte legend (one shadow byte represents 8 application bytes):
      o) t2 a! p/ W# m# b1 U; a
  35.   Addressable:           00
    / t+ I. x# R' x
  36.   Partially addressable: 01 02 03 04 05 06 07, L9 G" \& b) ~* a6 V6 ^5 `
  37.   Heap left redzone:       fa
    / ~& H6 h. ~0 H- ]* @. z1 q
  38.   Freed heap region:       fd
    0 s  j6 G* O- i( O) _6 s
  39.   Stack left redzone:      f16 y& u$ w  j5 e! d1 B0 R5 b1 H
  40.   Stack mid redzone:       f22 _# x5 f5 U7 K9 _
  41.   Stack right redzone:     f3
    * |, E! [: g8 O, C0 P
  42.   Stack after return:      f5$ ^* t8 B0 S; G. P/ I7 J
  43.   Stack use after scope:   f8
    $ V8 S: @9 L+ \5 U* F, _
  44.   Global redzone:          f9* U5 Q  ~3 d7 @9 d6 E0 h& f& b
  45.   Global init order:       f6# ~4 N1 k% B- G  s2 U( s) C" G+ m
  46.   Poisoned by user:        f7
    1 X) V* D( _9 Y  q
  47.   Container overflow:      fc& E$ ]0 W, e# }) v( f8 K
  48.   Array cookie:            ac5 @" `! U0 K% A2 Z! r7 w
  49.   Intra object redzone:    bb  d9 S4 q8 _' d2 Q0 k6 n0 A' E; r( ?: z
  50.   ASan internal:           fe" q' M4 v8 n& q
  51.   Left alloca redzone:     ca
    : m& l  i( V0 M0 e; k( l
  52.   Right alloca redzone:    cb
    2 S, F" m" H1 L4 x7 ?( [
  53. ==282==ABORTING
复制代码

) d4 T1 U$ x4 p  U# P  d" T
Initialization order bugs
示例,这里有两个文件:
  1. * b4 e* t6 E) K$ e
  2. // test_memory1.cc; @9 J/ G4 `# d+ o; w2 R- x: b
  3. #include <stdio.h>6 ]+ W/ p! N' E' c0 W% A! b

  4. - _! }4 A( ?) r; {2 D1 g
  5. extern int extern_global;
    $ d1 G5 q* M5 Y
  6. int read_extern_global() { return extern_global; }! z- L. \4 a  [: a

  7. 4 s; G4 x* a9 L0 M/ e. ~' c
  8. int x = read_extern_global() + 1;
    - @) _; T, T/ R+ s; Z5 C( s' Z
  9. 6 U$ y7 d. K3 V( e* {
  10. int main() {
    + X0 y8 _( n: s% M
  11.     printf("%d\n", x);3 y1 \6 }! o3 N- J. G) G5 M
  12.     return 0;
    9 u9 V4 g1 Y; c; V9 }, p; h
  13. }
复制代码
  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
    - O. ^2 H, P9 I
  2. // test_memory2.cc  Y; |' l/ t8 g

  3. 0 s+ t  t, N: ?0 i2 M6 @: I
  4. int foo() { return 123; }! d, g, K2 N6 C
  5. </font></font></font>
复制代码
第一种编译方式输出如下:

  1. / n8 P) \0 l6 o
  2. g++ test_memory1.cc test_memory2.cc && ./a.out2 j' ~+ j3 `5 ^8 K
  3. 1
复制代码
/ S& X: l* u* @- Y
第二种编译方式输出如下:

  1. 7 S; E% M; l: _2 `: W
  2. g++ test_memory2.cc test_memory1.cc && ./a.out
    * A# [) a# p- c2 x6 N9 d
  3. 124
复制代码
! e0 ?: \  ~- q" j# B
这种问题我们平时编程过程中可以都不会太注意,然而通过ASan可以检测出这种潜在的bug:5 r2 T+ t; p  v3 j
编译and输出:
  1. 6 e% s/ n- ?# t6 E7 h
  2. g++ -fsanitize=address -g test_memory1.cc test_memory2.cc
    ( H  c- q- f% I, w* ~+ ^3 e
  3. / m  u# P6 R/ M& l) r% v% y% n
  4. ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true ./a.out
    ( z9 F, _% u* a! O" O
  5. =================================================================
    - }0 s+ ]" t( @' y: |7 p: G* G6 W
  6. ==419==ERROR: AddressSanitizer: initialization-order-fiasco on address 0x7f46c20021a0 at pc 0x7f46c1e00c28 bp 0x7fffe423d920 sp 0x7fffe423d910
    * R# T' ^" V2 p& b$ N
  7. READ of size 4 at 0x7f46c20021a0 thread T0
    ; n6 \4 i* L" q3 u2 t
  8.     #0 0x7f46c1e00c27 in read_extern_global() /home/wangzhiqiang/test/test_memory1.cc:3: v) z8 _! y+ |# W- F) b
  9.     #1 0x7f46c1e00cb3 in __static_initialization_and_destruction_0 /home/wangzhiqiang/test/test_memory1.cc:4
    + i: B" O+ e. g! b8 E2 N
  10.     #2 0x7f46c1e00d0a in _GLOBAL__sub_I__Z18read_extern_globalv /home/wangzhiqiang/test/test_memory1.cc:8- y; m9 U9 {1 F+ i2 I( X
  11.     #3 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c)
    . l% R& x! J( H% ~" L# ?. P
  12.     #4 0x7f46c0461b27 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b27)/ p2 K1 `5 q8 T) {! g4 v3 i
  13.     #5 0x7f46c1e00b09 in _start (/mnt/d/wzq/wzq/util/test/a.out+0xb09)( u% Q) x2 r$ s: c. U8 h6 m' ]# U
  14. # C7 e( U5 b+ @1 G2 Q, N9 O6 q
  15. 0x7f46c20021a0 is located 0 bytes inside of global variable 'extern_global' defined in 'test_memory2.cc:2:5' (0x7f46c20021a0) of size 4+ d. R% Z/ X: H# r; F
  16.   registered at:
    % P; N! S6 n" H4 F0 Q( ?: _
  17.     #0 0x7f46c08764a8  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x364a8)/ _2 D6 Z: n; a* `
  18.     #1 0x7f46c1e00e0b in _GLOBAL__sub_I_00099_1__Z3foov (/mnt/d/wzq/wzq/util/test/a.out+0xe0b)! F, x- g! [; E' X' ^( q
  19.     #2 0x7f46c1e00e5c in __libc_csu_init (/mnt/d/wzq/wzq/util/test/a.out+0xe5c)/ @) a" u, }- J
  20. ! ~/ B' c- H9 i- t) G4 q
  21. SUMMARY: AddressSanitizer: initialization-order-fiasco /home/wangzhiqiang/test/test_memory1.cc:3 in read_extern_global()
    * e: D- S  s" n9 k2 I1 M: O
  22. Shadow bytes around the buggy address:% f* ~* G9 n# V+ t8 }( A, Y  b$ `
  23.   0x0fe9583f83e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ( D; ?5 @' O& {8 }% c- [
  24.   0x0fe9583f83f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00/ z0 I4 b. D6 c9 h* p
  25.   0x0fe9583f8400: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  J& j' S' e+ w& B! D
  26.   0x0fe9583f8410: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    4 Z; R1 S$ }0 G  b% c+ q
  27.   0x0fe9583f8420: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9
    / F. a* m% b& p8 _1 r" `4 |1 I4 k
  28. =>0x0fe9583f8430: 00 00 00 00[f6]f6 f6 f6 f6 f6 f6 f6 00 00 00 00# s1 b0 c* E/ n4 e8 ~0 {8 z
  29.   0x0fe9583f8440: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    ! T5 G- D) z$ n. U" |2 t
  30.   0x0fe9583f8450: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    % l5 H  z. V4 h3 X1 |
  31.   0x0fe9583f8460: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000 {' P- d1 G; f1 Y/ R
  32.   0x0fe9583f8470: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    5 X, ~1 b/ U1 E. V
  33.   0x0fe9583f8480: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    : J  ^) f( {; O
  34. Shadow byte legend (one shadow byte represents 8 application bytes):; g/ }9 V9 x( O9 l: D$ P6 c+ N, C
  35.   Addressable:           00
    2 Z9 S+ _% o. R+ f
  36.   Partially addressable: 01 02 03 04 05 06 07
    ( b) P5 q8 k; n  C- b
  37.   Heap left redzone:       fa
    4 F5 [8 I& G. M, U! _6 k
  38.   Freed heap region:       fd. d5 t  C( J  E+ D
  39.   Stack left redzone:      f1$ j% s" z0 ~) Q# T0 j$ ^, M
  40.   Stack mid redzone:       f2" U4 y" C( s3 K) v
  41.   Stack right redzone:     f3
      g2 O4 S  V; o2 M" }
  42.   Stack after return:      f5
    % w: [4 S& h3 S1 ^& m4 [
  43.   Stack use after scope:   f8" n1 S6 s( C* h4 W$ g; ?, I6 t
  44.   Global redzone:          f9
    6 V' |4 {% Z5 G+ L
  45.   Global init order:       f6/ B1 p8 u' s, i0 z: Z7 s
  46.   Poisoned by user:        f77 r- `, H4 K' l1 L& X
  47.   Container overflow:      fc/ }8 P6 C  y9 R* r9 c/ ~' x
  48.   Array cookie:            ac6 U) |9 R5 w- v5 Z6 x' k( W
  49.   Intra object redzone:    bb, S: \- Z( Z5 ^: }
  50.   ASan internal:           fe& C7 g0 r& l* ~% p% d) D3 f# b1 ]
  51.   Left alloca redzone:     ca
    + F1 E: y+ O7 a1 ]6 n. @& z/ u
  52.   Right alloca redzone:    cb7 ^! D- K7 V$ `" J1 Q. X& B
  53. ==419==ABORTING
复制代码
* K$ a- S& ~: `
注意:这里在运行程序前需要添加环境变量:
  1. & h  j" y9 B" v- w# V# X" C" K
  2. ASAN_OPTIONS=check_initialization_order=true:strict_init_order=true
复制代码

$ D/ @" M- V, l1 @; T
小总结
ASan是个很好的检测内存问题的工具,不需要配置环境,使用还方便,编译时只需要-fsanitize=address -g就可以,运行程序时候可以选择添加对应的ASAN_OPTIONS环境变量就可以检测出很多内存问题。它的错误信息也很有用,明确指出当前是什么类型的内存错误,如:
  • detected memory leaks
  • heap-buffer-overflow
  • stack-buffer-overflow
  • global-buffer-overflow
  • heap-use-after-free
  • initialization-order-fiasco
    + K4 N% N# h5 o4 S
赞 收藏 1 评论1 发布时间:2020-5-26 22:52

举报

1个回答
慎微 回答时间:2020-5-27 10:47:20
以前,内存泄漏是个大难题,现在 gcc 加个编译开关就行!? 有 malloc 没配对 free 可直接查到?
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版