$30 off During Our Annual Pro Sale. View Details »

Мойте руки перед едой, или Санитайзеры в тестировании

Мойте руки перед едой, или Санитайзеры в тестировании

https://youtu.be/Aeu7abIKgGs

http://2017.heisenbug-piter.ru/talks/wash-your-hands-before-eating-or-sanitizer-in-testing/

https://asatarin.github.io/talks/sanitizers-in-testing/

Как известно, «с большой силой приходит и большая ответственность». С++ – это язык с большой выразительной силой и огромными возможностями. За эти возможности приходится платить потенциальными дефектами, которые отсутствуют в программах на управляемых (managed) языках.

Санитайзеры – замечательные инструменты, которые позволяют находить сложные дефекты в программах на C++. Я расскажу об этих инструментах, их возможностях и о том, как их использовать с пользой для своего проекта.

Andrey Satarin

June 04, 2017
Tweet

More Decks by Andrey Satarin

Other Decks in Technology

Transcript

  1. View Slide

  2. Андрей Сатарин
    Мойте руки перед едой,
    или Санитайзеры
    в тестировании

    View Slide

  3. 〉Что такое санитайзеры?
    〉Address Sanitizer
    〉Устройство Address Sanitizer
    〉Memory Sanitizer
    〉Thread Sanitizer
    Не буду рассказывать какие нужны ключи и как интегрировать
    в вашу сборку
    О чем я сегодня расскажу
    3

    View Slide

  4. Что такое санитайзеры?

    View Slide

  5. 〉Инструменты для динамического поиска дефектов кода на C++
    〉Динамический — значит работают на запущенном коде,
    например в тестах
    〉Требуют специальной компиляции кода программы (работает
    в GCC, Clang)
    〉Поддерживаются на Linux x86_64
    Что такое санитайзеры?
    5

    View Slide

  6. 6
    Компиляция тестов 

    с санитайзерами
    Запуск тестов
    Новые дефекты 

    найденные санитайзерами

    View Slide

  7. Зачем нам нужны
    специальные
    инструменты для C++?

    View Slide

  8. 〉Переполнение буфера (buffer overflow)
    〉Использование после освобождения (use after free)
    〉Использование не инициализированного значение
    (uninitialized value)
    Ошибки работы с памятью
    8

    View Slide

  9. CVE — Common Vulnerabilities and Exposures
    http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=buffer+overflow
    Search Results:
    There are 8322 CVE entries that match your search.
    CVE: buffer overflow
    9

    View Slide

  10. This is based on the Eternalblue tool
    stolen from the NSA, and was
    developed by infosec biz RiskSense.
    It reveals that the SMB server bug
    is the result of a buffer overflow
    in Microsoft's code. [WC]
    WannaCrypt
    10

    View Slide

  11. CVE — Common Vulnerabilities and Exposures
    http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=use+after+free
    Search Results:
    There are 1006 CVE entries that match your search.
    CVE: use after free
    11

    View Slide

  12. CVE — Common Vulnerabilities and Exposures
    http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=uninitialized+memory
    Search Results
    There are 202 CVE entries that match your search.
    CVE: uninitialized memory
    12

    View Slide

  13. — Do you know the first
    thing about bug finding?
    — Stick ’em with the pointy
    end.
    — That’s the essence of it.
    13

    View Slide

  14. Address Sanitizer

    View Slide

  15. C++
    int* a =
    new int[10];
    a[10] = 1;
    // nothing
    Buffer overflow: C++ vs Java
    15
    Java
    int[] a =
    new int[10];
    a[10] = 1;
    // AIOOBException

    View Slide

  16. Пример 1

    View Slide

  17. int sum(int* array, int lo, int hi) {
    int res = 0;
    for (int i = lo; i <= hi; i++) {
    res += array[i];
    }
    return res;
    }
    int main(int argc, char **argv) {
    int *array = new int[10] {0, …, 9};
    int res = sum(array, argc, 10);
    delete [] array;
    return res;
    }
    17

    View Slide

  18. AddressSanitizer: heap-buffer-overflow on address …

    READ of size 4 at … thread T0
    #0 … in sum(int*, int, int) /heap_buffer_overflow.cpp:6:16

    #1 … in main /heap_buffer_overflow.cpp:14
    … is located 0 bytes to the right of 40-byte region […,…) allocated
    by thread T0 here:

    #0 …

    #1 … in main /heap_buffer_overflow.cpp:13:18
    SUMMARY: AddressSanitizer: heap-buffer-overflow 

    /heap_buffer_overflow.cpp:6:16 in sum(int*, int, int)
    Address Sanitizer: heap-buffer-overflow
    18

    View Slide

  19. int sum(int* array, int lo, int hi) {
    int res = 0;
    for (int i = lo; i <= hi; i++) {
    res += array[i];
    }
    return res;
    }
    int main(int argc, char **argv) {
    int *array = new int[10] {0, …, 9};
    int res = sum(array, argc, 10);
    delete [] array;
    return res;
    }
    19
    heap-buffer-overflow
    allocated here

    View Slide

  20. Пример 2

    View Slide

  21. int fib(int n) {
    int *arr = new int[n + 2] {0};
    arr[0] = 1;
    arr[1] = 1;
    for (int i=2; i < n; i++) {
    arr[i] = arr[i - 1] + arr[i - 2];
    }
    int *x = &arr[n - 1];
    delete [] arr;
    return *x;
    }
    21

    View Slide

  22. AddressSanitizer: heap-use-after-free on address …

    READ of size 4 at … thread T0

    #0 … in fib(int) /heap_use_after_free.cpp:12:12

    …

    … is located 0 bytes inside of 12-byte region […,…) freed by thread
    T0 here:

    #0 …

    #1 … in fib(int) /heap_use_after_free.cpp:11:5

    previously allocated by thread T0 here:

    #0 …

    #1 … in fib(int) /heap_use_after_free.cpp:4:18
    SUMMARY: AddressSanitizer: heap-use-after-free 

    /heap_use_after_free.cpp:12:12 in fib(int)
    Address Sanitizer: heap-use-after-free
    22

    View Slide

  23. int fib(int n) {
    int *arr = new int[n + 2] {0};
    arr[0] = 1;
    arr[1] = 1;
    for (int i=2; i < n; i++) {
    arr[i] = arr[i - 1] + arr[i - 2];
    }
    int *x = &arr[n - 1];
    delete [] arr;
    return *x;
    }
    23
    freed by thread T0 here
    heap-use-after-free

    View Slide

  24. Устройство Address
    Sanitizer

    View Slide

  25. 〉Инструментация при компиляции — добавляем проверки на
    каждое чтение/запись
    〉Runtime библиотека для проверки доступов
    〉Специальная «теневая» область памяти для отслеживания
    состояния памяти (shadow memory)
    Как это все работает?
    25

    View Slide

  26. *address = …;
    Инструментация при компиляции
    26
    if (IsPoisoned(address)) {

    ReportError(…);

    }

    *address = …;

    View Slide

  27. 〉Подменяет malloc/free
    〉malloc создает «красные зоны» при аллокации
    〉free «отравляет» (poisons) освобожденные регионы памяти
    и держит их в карантине
    Runtime библиотека
    27

    View Slide

  28. 〉На 8 байт памяти создается 1 байт «теневой» памяти
    〉Содержит метаданные о памяти вашего приложения
    〉«Отравление» (poisoning) блока основной памяти —
    специальная метка в теневой памяти, соответствующей этому
    блоку основной памяти
    Теневая память (Shadow memory)
    28

    View Slide

  29. Shadow byte legend (one shadow byte represents
    8 application bytes):

    Addressable: 00

    Partially addressable: 01 02 03 04 05 06 07

    Heap left redzone: fa

    Heap right redzone: fb

    Freed heap region: fd


    Теневая память (Shadow memory)
    29

    View Slide

  30. Пример 3

    View Slide

  31. int main(int argc, char **argv) {
    int *array = new int[10] {0};
    int *x = &array[argc];
    delete [] array;
    return *x;
    }
    Детектирование use-after-free
    31

    View Slide

  32. Shadow memory:

    0x9bd0: fa fa fa fa fa fa fa fa fa fa

    0x9be0: fa fa fa fa fa fa fa fa fa fa

    0x9bf0: fa fa fa fa 00 00 00 00 00 fa

    0x9c00: fa fa fa fa fa fa fa fa fa fa

    0x9c20: fa fa fa fa fa fa fa fa fa fa
    fa — Heap left redzone
    int *array = new int[10] {0};
    32

    View Slide

  33. int *x = &array[argc];
    33
    Shadow memory:

    0x9bd0: fa fa fa fa fa fa fa fa fa fa

    0x9be0: fa fa fa fa fa fa fa fa fa fa

    0x9bf0: fa fa fa fa 00 00 00 00 00 fa

    0x9c00: fa fa fa fa fa fa fa fa fa fa

    0x9c20: fa fa fa fa fa fa fa fa fa fa
    00 — Addressable

    View Slide

  34. delete [] array;
    34
    Shadow memory:

    0x9bd0: fa fa fa fa fa fa fa fa fa fa

    0x9be0: fa fa fa fa fa fa fa fa fa fa

    0x9bf0: fa fa fa fa fd fd fd fd fd fa

    0x9c00: fa fa fa fa fa fa fa fa fa fa

    0x9c20: fa fa fa fa fa fa fa fa fa fa
    fd — Freed heap region

    View Slide

  35. return *x;
    35
    Shadow memory:

    0x9bd0: fa fa fa fa fa fa fa fa fa fa

    0x9be0: fa fa fa fa fa fa fa fa fa fa

    0x9bf0: fa fa fa fa[fd]fd fd fd fd fa

    0x9c00: fa fa fa fa fa fa fa fa fa fa

    0x9c20: fa fa fa fa fa fa fa fa fa fa
    fd — Freed heap region

    View Slide

  36. AddressSanitizer: heap-use-after-free on address …

    READ of size 4 at … thread T0

    #0 … in main /heap_use_after_free.cpp:8:12
    … is located 4 bytes inside of 40-byte region … here:

    #0 …

    #1 … in main /heap_use_after_free.cpp:7:5
    previously allocated by thread T0 here:

    #0 …

    #1 … in main /heap_use_after_free.cpp:5:18
    SUMMARY: AddressSanitizer: heap-use-after-free 

    /heap_use_after_free.cpp:8:12 in main
    Итоговый отчет
    36

    View Slide

  37. int main(int argc, char **argv) {
    int *array = new int[10] {0};
    int *x = &array[argc];
    delete [] array;
    return *x;
    }
    Детектирование use-after-free
    37
    READ of size 4
    inside of 40-byte region

    View Slide

  38. 〉Практически нет false positive ошибок
    〉Высокая точность, если код сделал «что-то плохое» — это
    будет обнаружено
    〉Проще всего для первоначального внедрения в проекте
    〉В наших тестах нет замедления, обычно ~2x
    Address Sanitizer: итоги
    38

    View Slide

  39. Memory Sanitizer

    View Slide

  40. C++
    int* a =
    new int[10];
    a[0] // == ???
    Не инициализированная память: Java vs C++
    40
    Java
    int[] a =
    new int[10];
    a[0] // == 0

    View Slide

  41. Пример 4

    View Slide

  42. int main(int argc, char** argv) {
    int* a = new int[10] {0};
    int* b = new int[10];
    memcpy(b, a, 10);
    int res = b[argc + 5];
    delete [] a;
    delete [] b;
    return res;
    }
    42

    View Slide

  43. MemorySanitizer: use-of-uninitialized-value
    #0 … in main /uninitialized-memory.cpp:17:5

    SUMMARY: MemorySanitizer: use-of-uninitialized-value 

    /uninitialized-memory.cpp:17:5 in main
    Memory Sanitizer: Use-of-uninitialized-value
    43

    View Slide

  44. int main(int argc, char** argv) {
    int* a = new int[10] {0};
    int* b = new int[10];
    memcpy(b, a, 10);
    int res = b[argc + 5];
    delete [] a;
    delete [] b;
    return res;
    }
    44
    use-of-uninitialized-value
    Проблемная

    память

    View Slide

  45. MemorySanitizer: use-of-uninitialized-value

    #0 … in main /uninitialized-memory.cpp:17:5


    Uninitialized value was created by a heap allocation

    #0 …

    #1 … in main /uninitialized-memory.cpp:12:14
    SUMMARY: MemorySanitizer: use-of-uninitialized-value 

    /uninitialized-memory.cpp:17:5 in main
    Memory Sanitizer: origin tracking
    45

    View Slide

  46. int main(int argc, char** argv) {
    int* a = new int[10] {0};
    int* b = new int[10];
    memcpy(b, a, 10);
    int res = b[argc + 5];
    delete [] a;
    delete [] b;
    return res;
    }
    46
    use-of-uninitialized-value
    Heap allocation

    View Slide

  47. __msan_unpoison(a, size);
    __msan_poison(a, size);
    __msan_check_mem_is_initialized(
    a, size
    );
    Memory Sanitizer: тонкая настройка
    47

    View Slide

  48. 〉Memory Sanitizer находит только одну проблему — это не
    делает его менее полезным
    〉Не инициализированная память может приводить
    к произвольным последствиям в коде
    〉В сложных случаях может помочь origins tracking и тонкая
    настройка
    Memory Sanitizer: итоги
    48

    View Slide

  49. Важны ли дефекты
    найденные
    санитайзерами?

    View Slide

  50. Было:
    〉несколько дефектов найденных Address/Memory Sanitizer
    〉странные баги, которые было непонятно как чинить
    => приняли решение починить все дефекты найденные
    санитайзерами
    Важны ли дефекты от санитайзеров?
    50

    View Slide

  51. Стало:
    〉все проблемы, найденные Address/Memory Sanitizer были
    исправлены
    〉странные баги пропали, хотя напрямую их никто не чинил
    Важны ли дефекты от санитайзеров?
    51

    View Slide

  52. Thread Sanitizer

    View Slide

  53. Состояние гонки является
    классическим гейзенбагом.
    Состояние гонки возникает тогда, когда
    несколько потоков многопоточного
    приложения пытаются одновременно
    получить доступ к данным, причем хотя
    бы один поток выполняет запись [DR].
    Состояние гонки (race condition/data race)
    53

    View Slide

  54. CVE — Common Vulnerabilities and Exposures
    http://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=race+condition
    Search Results
    There are 580 CVE entries that match your search.
    CVE: race condition
    54

    View Slide

  55. Therac-25
    55

    View Slide

  56. Пример 5

    View Slide

  57. void Init() {
    if (!inited) {
    mutex.lock();
    if (!inited) {
    Global = 1;
    }
    inited = true;
    mutex.unlock();
    }
    }
    57

    View Slide

  58. ThreadSanitizer: data race
    Write of size 1 at … by main thread (mutexes: write M7):
    #0 Init() /dcl.cpp:17:12
    Previous read of size 1 at … by thread T1:
    #0 Init() /dcl.cpp:11:8
    Location is global 'inited' of size 1 at …
    Mutex M7 (…) created at:

    SUMMARY: ThreadSanitizer: data race /dcl.cpp:17:12 in Init()
    Thread Sanitizer: data race
    58

    View Slide

  59. void Init() {
    if (!inited) {
    mutex.lock();
    if (!inited) {
    Global = 1;
    }
    inited = true;
    mutex.unlock();
    }
    }
    59
    Write of size 1
    Previous read of size 1

    View Slide

  60. 〉Дефекты от Thread Sanitizer чинят с наибольшей неохотой —
    лучше внедрять его последним из всех
    〉Не 100% точен — тесты с ним «мигают»
    〉На нашем коде из всех троих больше всего замедляет тесты
    〉Требует много (~5x) памяти by design
    Thread Sanitizer: итоги
    60

    View Slide

  61. Частые проблемы
    и решения

    View Slide

  62. Санитайзеры тормозят
    и ломают тесты

    View Slide

  63. constexpr TDuration TIMEOUT
    = NSan::PlainOrUnderSanitizer(
    TDuration::Seconds(120),
    TDuration::Seconds(240)
    );
    Тесты тормозят — что делать?
    63

    View Slide

  64. inline constexpr static T PlainOrUnderSanitizer(
    T plain, T sanitized
    ) noexcept {
    #if defined(_tsan_enabled_)
    || defined(_msan_enabled_)
    || defined(_asan_enabled_)
    return sanitized;
    #else
    return plain;
    #endif
    }
    Тесты тормозят — что делать?
    64

    View Slide

  65. Это прекрасно, но мы
    не пишем на C++

    View Slide

  66. 〉go race — это Thread Sanitizer работающий в Go [GO1][GO2]
    〉go -msan — Memory Sanitizer для Go [GO3]

    «Such interoperation is useful mainly for testing a program
    containing suspect C or C++ code»
    〉В компилятор языка Rust с февраля 2017 включена поддержка
    Address, Leak, Memory, Thread санитайзеров [RST]
    〉Для Java есть инструмент поиска дедлоков (deadlock) от
    компании Devexperts [DL]
    Санитайзеры на других платформах
    66

    View Slide

  67. Наша статистика
    67
    Address Sanitizer 59 дефектов
    Memory Sanitizer 26 дефектов
    Thread Sanitizer 52 дефекта

    View Slide

  68. View Slide

  69. 〉Санитайзеры можно использовать не зная C++
    〉Внедрение лучше начинать с Address Sanitizer
    〉Если вы еще не используете санитайзеры, в вашем коде
    100% есть дефекты, которые они найдут
    〉Разные санитайзеры находят разные дефекты — надо
    использовать их все
    〉Использование санитайзеров в тестировании — простой
    и дешевый способ решения сложной проблемы
    Выводы
    69

    View Slide

  70. All bugs must die

    View Slide

  71. Андрей Сатарин
    Ведущий инженер по автоматизации тестирования
    https://twitter.com/asatarin
    [email protected]

    View Slide

  72. 〉«AddressSanitizer: A Fast Address Sanity Checker»
    〉« MemorySanitizer: fast detector of uninitialized memory 

    use in C++»
    〉«ThreadSanitizer: data race detection in practice»
    〉https://github.com/google/sanitizers
    Ссылки
    72

    View Slide

  73. 〉AddressSanitizer, или как сделать программы на C/С++
    надежнее и безопаснее
    〉""go test -race" Under the Hood" by Kavya Joshi
    〉Konstantin Serebryany
    Ссылки
    73

    View Slide

  74. 〉https://www.flickr.com/photos/16210667@N02/8681651088/
    〉Therac-25 http://www.cs.umd.edu/class/spring2003/cmsc838p/
    Misc/therac.pdf
    〉https://www.flickr.com/photos/43326207@N00/4810894062/
    Credits
    74

    View Slide