Memory sanitizer

11
MemorySanitizer: fast detector of uninitialized memory use in C++ 2015/7/8 光成滋生

Transcript of Memory sanitizer

Page 1: Memory sanitizer

MemorySanitizer:

fast detector of uninitialized memory use in C++

2015/7/8

光成滋生

Page 2: Memory sanitizer

C/C++の未初期化メモリ実行時チェックツール

http://research.google.com/pubs/pub43308.html の紹介

動作速度のコストは2.5x, メモリ使用量は2x程度

Clangに組み込まれていて-fsanitize=memoryで利用可能

未初期化メモリ(UUM : Use of Uninitialized Memory)の

トラッキングも可能

実際にGoogleのたくさんの開発で利用されている

2/11

MemorySanitizer(以下MSan)

Page 3: Memory sanitizer

valgrind(Memcheck)

UUM、ヒープオーバーフロー、free後の利用の検出

1byteに2bitの情報

addressable+init, not addr, addr+uninit, addr+partially init

false positiveが0に近い

20x遅い(マルチスレッドでもっと遅い)

Dr. Memory

Memcheckの2xほど

マルチスレッドでちょっとfalse positive

Intel Inspector XE

遅い

3/11

Related work

Page 4: Memory sanitizer

各1bitに1bit割り当てる(0:init/defined, 1:not)

Shadow = App & (~0x4000|032)

Originはトラッキングのときに使う

どちらもMAP_NORESERVEをつける(Swap対象としない)

4/11

Shadow memory

Application

Origin

Shadow

Protected 0x0000|032

0x2000|032

0x4000|032

0x6000|032

0x7fff|132

Page 5: Memory sanitizer

UM(未初期化メモリ)は伝達する

C++11では未初期化objectのlvalueからrvalueへの変換は未定義だった

C++14でindeterminate valueに緩和される

struct A { char x; int y; };を8byteのmemcpyに最適化されてもエラーを出さないように

UMにしたがって条件分岐、システムコールの呼び出し、ポインタdereferenceしたときにエラーを出力

5/11

Shadow propagation

Page 6: Memory sanitizer

false positiveを出さない方針

その条件の元でできるだけfalse negativeを減らす

記号 : A = B op C(オリジナル), A', B', C':shadow

6/11

Shadow propagation rule

命令 ルール

A = load P check P', load (P & shadowMask)

*P = A check P', *(P & shadowMask) = A'

A = const A' = 0

A = undef A' = 0xff

A = B & C A' = (B'&C')|(B&C')|(B'&C)

A = B | C A' = (B'&C')|(~B&C')|(B'&~C)

A = B ^ C A' = B' | C'

A = B << C A' = (sign extend(C'!=0))|(B'<<C)

Page 7: Memory sanitizer

A = B * (C * (1 << D))

A' = B' << Dで代用(一般はA' = B'| C')

bitwiseにやってるとfalse positiveになる例

clangはこれを *(unsigned char*)s > 7と最適化する

X : unsigned, X':対応するshadowのとき

Xのとり得る範囲は[VMin(X, X'), VMax(X, X')]

VMin(X, X') = X & (~X')

VMax(X, X') = X | X'

正確だがベンチマークで50%の速度低下

7/11

複雑なパターン(1/2)

struct S { int a:3; int b:5; }; bool f(S* s) { return s->b; }

Page 8: Memory sanitizer

等号比較

A = B == Cを

D = B ^ C, A = D == 0に変換

D' = B'|C', A'=(!(D & ~D')) && (D' != 0)

条件演算子

A = B ? C : D

BがundefでもCとDが同じでdefならAはdefになる

A' = B' ? ((C ^ D) | C' | D') : (B ? C' : D')

8/11

複雑なパターン(2/2)

Page 9: Memory sanitizer

vector instruction

同じ数だけ並べる

thread safety

global lockは遅いので別のやりかたで

CAS, RMWは以前の値は全てinitializedと仮定

function calls

va_listの扱いが大変

run-time library

300ほどの関数をintercept

9/11

その他

Page 10: Memory sanitizer

valgrindの10倍ぐらい速い

10/11

performance

Page 11: Memory sanitizer

未初期化参照が分かってもそれがどこにあったか分からないと辛い

源流を保持する

11/11

origin tracking

#include <stdio.h> int arr[2]; void shift() { arr[1] = arr[0]; } void push(int *p) { shift(); arr[0] = *p; } int pop() { int x = arr[1]; shift(); return x; } void func1() { int local_var; push(&local_var); } int main() { func1(); shift(); return pop(); }

clang-3.5 -fsanitize-memory-track-origins=2 -g -fsanitize=memory t.c && ./a.out ==22068== WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x7f87ee79de13 (/home/shigeo/a.out+0x8ee13) #1 0x7f87ed600ec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4) #2 0x7f87ee79d7bc (/home/shigeo/a.out+0x8e7bc) ... Uninitialized value was created by an allocation of 'local_var' in the stack frame of function 'func1' #0 0x7f87ee79dce0 (/home/shigeo/a.out+0x8ece0) SUMMARY: MemorySanitizer: use-of-uninitialized-value ??:0 ?? ちなみにgcc t.c -Wall –Wextraでは警告は何もでない。 % valgrind --track-origins=yes ./a.out ==22149== Syscall param exit_group(status) contains uninitialised byte(s) ==22149== at 0x4EF8309: _Exit (_exit.c:32) ==22149== by 0x4E7321A: __run_exit_handlers (exit.c:97) ==22149== by 0x4E732A4: exit (exit.c:104) ==22149== by 0x4E58ECB: (below main) (libc-start.c:321) ==22149== Uninitialised value was created by a stack allocation ==22149== at 0x400560: func1 (t.c:13)