Возникла сложная ситуация.
Есть код, написанный на C, который активно используется через cgo в проекте, написанном на Go. В какой-то момент программа начала вылетать с ошибками от malloc: то ли segfault, то ли повреждение памяти.
Логичная идея: нужен valgrind с его memcheck, чтобы проверить, кто возится с папочкой в чертову невалидную память.
Однако попытка скормить Walgrind бинарник, полученный от go-сборки, приведет лишь к разочарованию — даже на простом Hello World Walgrind выдаст сотни ошибок и отправит разработчика по известным координатам (спойлер: " Иди исправляй свою программу! ").
Это связано с тем, что среда выполнения go довольно специфична и существенно отличается от таковой в C. (Подробности можно легко найти, введя в поиск «golang valgrind»).
Так как же нам понять, что происходит? Более близкий поиск в Google показал мне, что у gcc (и clang, кстати) есть очень удобный инструмент — Адресное дезинфицирующее средство .
Это удобно, потому что он может делать то, что умеет valgrind (как минимум, он может отлавливать использование освобожденной памяти, переполнения и утечки), и при этом автоматически интегрируется в бинарник без необходимости использования внешних утилит. .
Но главное — его можно смело использовать в CGo для отладки кода на языке C, не вмешиваясь в среду выполнения Go (собственно, сами разработчики Go рекомендуют использовать этот инструмент).
Как это использовать?
- Убедитесь, что наша версия gcc не ниже 4.8.
- Скомпилируйте нашу программу с флагом -fsanitize=address (многие другие дезинфицирующие средства описаны в документации gcc, вы можете увидеть здесь ).
Этот флаг необходимо добавлять как во время сборки, так и во время компоновки (как в CFLAGS, так и в LDFLAGS).
- Запустите программу и наслаждайтесь разноцветным результатом работы дезинфицирующего средства.
Мы компилируем это с -fsanitize=address, после запуска видим следующее:#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int *m = (int *) malloc(10 * sizeof (int)); for (int i = 0; i < 11; i++) { m[i] = i; } return 0; }
webconn@webconn-laptop:~/Projects/Testing/C/Sanitizer$ gcc -o .
/main .
/test.c -fsanitize=address
webconn@webconn-laptop:~/Projects/Testing/C/Sanitizer$ .
/main
=================================================================
==18098==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x557945c74922 bp 0x7fff94c67d40 sp 0x7fff94c67d38
WRITE of size 4 at 0x60400000dff8 thread T0
#0 0x557945c74921 in main (/home/webconn/Projects/Testing/C/Sanitizer/main+0x921)
#1 0x7fea6440b2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#2 0x557945c747a9 in _start (/home/webconn/Projects/Testing/C/Sanitizer/main+0x7a9)
0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8)
allocated by thread T0 here:
#0 0x7fea6484ad28 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1d28)
#1 0x557945c748c8 in main (/home/webconn/Projects/Testing/C/Sanitizer/main+0x8c8)
#2 0x7fea6440b2b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/webconn/Projects/Testing/C/Sanitizer/main+0x921) in main
Shadow bytes around the buggy address:
0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa]
0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==18098==ABORTING
На самом деле, его еще довольно удобно выделить цветами: Цветной вывод (PNG, 78,5 КБ)
Чтобы это работало с cgo, я добавил соответствующие специальные комментарии при подключении кода C (на месте многоточий могут быть ваши полезные флаги и директивы):
/*
#cgo linux LDFLAGS: .
-fsanitize=address
#cgo linux CFLAGS: .
-fsanitize=address
.
*/
import "C"
Что дало желаемый результат — теперь модуль, написанный на C, предоставляет отладочную информацию в случае проблем с памятью, но не мешает работе Go. На самом деле, сами разработчики Go рекомендуют в таких ситуациях использовать Sanitizer. Надеюсь, информация будет кому-то полезна.
UPD: Конечно, если вы добавите флаг -g, вывод станет еще немного читабельнее.
Вывод после компиляции с флагом -g webconn@webconn-laptop:~/Projects/Testing/C/Sanitizer$ .
/main
=================================================================
==12266==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60400000dff8 at pc 0x55d4f8d03922 bp 0x7ffc10a57370 sp 0x7ffc10a57368
WRITE of size 4 at 0x60400000dff8 thread T0
#0 0x55d4f8d03921 in main /home/webconn/Projects/Testing/C/Sanitizer/main.c:14
#1 0x7fa688d282b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
#2 0x55d4f8d037a9 in _start (/home/webconn/Projects/Testing/C/Sanitizer/main+0x7a9)
0x60400000dff8 is located 0 bytes to the right of 40-byte region [0x60400000dfd0,0x60400000dff8)
allocated by thread T0 here:
#0 0x7fa689167d28 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.3+0xc1d28)
#1 0x55d4f8d038c8 in main /home/webconn/Projects/Testing/C/Sanitizer/main.c:11
#2 0x7fa688d282b0 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x202b0)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/webconn/Projects/Testing/C/Sanitizer/main.c:14 in main
Shadow bytes around the buggy address:
0x0c087fff9ba0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bb0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9bd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9be0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c087fff9bf0: fa fa fa fa fa fa fa fa fa fa 00 00 00 00 00[fa]
0x0c087fff9c00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c087fff9c40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==12266==ABORTING
Теги: #valgrind #cgo #sanitize #компиляторы #C++ #Go
-
Подход К Обработке Онлайн-Анализа
19 Oct, 24 -
Hp Взорвала Свой Дата-Центр
19 Oct, 24 -
Государство 2.0
19 Oct, 24