Адресная санация модулей Boost.Python

Мой проект включает в себя большую библиотеку C++ и привязки Python (через Boost.Python). Набор тестов в основном написан поверх привязок Python, и я хотел бы запустить его с дезинфицирующими средствами, начиная с ASAN.

Я использую macOS (10.13.1 FWIW, но у меня были проблемы и с предыдущими версиями), и я не могу найти способ запустить ASAN на модулях Python (я очень сомневаюсь, что это связано с Boost.Python , я полагаю, то же самое с другими методами).

Вот простой модуль Python:

// hello_ext.cc
#include <boost/python.hpp>

char const* greet()
{
  auto* res = new char[100];
  std::strcpy(res, "Hello, world!");
  delete [] res;
  return res;
}

BOOST_PYTHON_MODULE(hello_ext)
{
  using namespace boost::python;
  def("greet", greet);
}

вот Makefile, который я использовал, сделанный для MacPorts:

// Makefile
CXX = clang++-mp-4.0
CXXFLAGS = -g -std=c++14 -fsanitize=address -fno-omit-frame-pointer
CPPFLAGS = -isystem/opt/local/include $$($(PYTHON_CONFIG) --includes)
LDFLAGS = -L/opt/local/lib
PYTHON = python3.5
PYTHON_CONFIG = python3.5-config
LIBS = -lboost_python3-mt $$($(PYTHON_CONFIG) --ldflags)

all: hello_ext.so

hello_ext.so: hello_ext.cc
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -shared -o $@ $< $(LIBS)

check: all
        $(ENV) $(PYTHON) -c 'import hello_ext; print(hello_ext.greet())'

clean:
        -rm -f hello_ext.so

Без асан все работает хорошо (ну, на самом деле, слишком хорошо...). Но с ASAN я столкнулся с LD_PRELOAD подобными проблемами:

$ make check
python -c 'import hello_ext; print(hello_ext.greet())'
==19013==ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen). Please launch the executable with:
DYLD_INSERT_LIBRARIES=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
"interceptors not installed" && 0make: *** [check] Abort trap: 6

Хорошо, давайте сделаем это: определите DYLD_INSERT_LIBRARIES

$ DYLD_INSERT_LIBRARIES=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib \
  python -c 'import hello_ext; print(hello_ext.greet())'
==19023==ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen). Please launch the executable with:
DYLD_INSERT_LIBRARIES=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
"interceptors not installed" && 0zsh: abort      DYLD_INSERT_LIBRARIES= python -c 'import hello_ext; print(hello_ext.greet())'

Давайте подозревать SIP, поэтому я отключил здесь SIP, и разрешим символические ссылки:

$ DYLD_INSERT_LIBRARIES=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib \
  /opt/local/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5 -c 'import hello_ext; print(hello_ext.greet())'
==19026==ERROR: Interceptors are not working. This may be because AddressSanitizer is loaded too late (e.g. via dlopen). Please launch the executable with:
DYLD_INSERT_LIBRARIES=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
"interceptors not installed" && 0zsh: abort      DYLD_INSERT_LIBRARIES=  -c 'import hello_ext; print(hello_ext.greet())'

Как правильно это сделать? Я также пытался загрузить libasan с помощью ctypes.PyDLL, и даже с sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL) я не могу заставить это работать.


person akim    schedule 03.12.2017    source источник


Ответы (2)


Итак, мне наконец удалось заставить это работать:

$ libasan=/opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
$ python=/opt/local/Library/Frameworks/Python.framework/Versions/3.5/Resources/Python.app/Contents/MacOS/Python
$ DYLD_INSERT_LIBRARIES=$libasan $python -c 'import hello_ext; print(hello_ext.greet())'
=================================================================
==70859==ERROR: AddressSanitizer: heap-use-after-free on address 0x60b000002770 at pc 0x000108c2ef60 bp 0x7ffee6fe8c20 sp 0x7ffee6fe83c8
READ of size 2 at 0x60b000002770 thread T0
    #0 0x108c2ef5f in wrap_strlen (libclang_rt.asan_osx_dynamic.dylib:x86_64h+0x14f5f)
    #1 0x109a8d939 in PyUnicode_FromString (Python:x86_64+0x58939)
[...]

Что изменилось? Ничего в цепочке компиляции, только вызов.

Пусть PYDIR=/opt/local/Library/Frameworks/Python.framework/Versions/3.5: раньше я звонил $PYDIR/bin/python3.5 (потому что /opt/local/bin/python3.5 является симлинком на него), теперь звоню $PYDIR/Resources/Python.app/Contents/MacOS/Python.

Чтобы понять, что происходит, я запустил DYLD_INSERT_LIBRARIES=$libasan python3.5 и поискал открытые файлы

$ ps
  PID TTY           TIME CMD
  900 ttys000    0:07.96 -zsh
70897 ttys000    0:00.11 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/Resources/Python.app/Contents/MacOS/Python
53528 ttys001    0:05.14 -zsh
  920 ttys002    0:10.28 -zsh
$ lsof -p 70897
COMMAND   PID USER   FD   TYPE DEVICE   SIZE/OFF       NODE NAME
Python  70897 akim  cwd    DIR    1,4        480 8605949500 /Users/akim/src/lrde/vcsn/experiment/sanitizer
Python  70897 akim  txt    REG    1,4      12988 8591019542 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/Resources/Python.app/Contents/MacOS/Python
Python  70897 akim  txt    REG    1,4    2643240 8591012758 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/Python
Python  70897 akim  txt    REG    1,4     107524 8590943656 /opt/local/lib/libintl.8.dylib
Python  70897 akim  txt    REG    1,4    2097528 8590888556 /opt/local/lib/libiconv.2.dylib
Python  70897 akim  txt    REG    1,4      20224 8591016920 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload/_heapq.cpython-35m-darwin.so
Python  70897 akim  txt    REG    1,4     326996 8591375651 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/readline/gnureadline.cpython-35m-darwin.so
Python  70897 akim  txt    REG    1,4     603008 8605907803 /opt/local/lib/libncurses.6.dylib
Python  70897 akim  txt    REG    1,4     837248 8606849556 /usr/lib/dyld
Python  70897 akim  txt    REG    1,4 1155837952 8606860187 /private/var/db/dyld/dyld_shared_cache_x86_64h
Python  70897 akim    0u   CHR   16,0  0t2756038        667 /dev/ttys000
Python  70897 akim    1u   CHR   16,0  0t2756038        667 /dev/ttys000
Python  70897 akim    2u   CHR   16,0  0t2756038        667 /dev/ttys000

Явно либасана тут нет, вот и вся проблема. Однако я также заметил, что ps относится к другому Python, а не к тому, который я запускал (и, конечно же, это часть открытых файлов).

Оказывается, в этом каталоге есть несколько исполняемых файлов Python: $PYDIR/bin/python3.5 и $PYDIR/Resources/Python.app/Contents/MacOS/Python, и первый так или иначе рикошетит ко второму. Если я запущу второй с DYLD_INSERT_LIBRARIES=$libasan

$ lsof -p 71114
COMMAND   PID USER   FD   TYPE DEVICE  SIZE/OFF       NODE NAME
Python  71114 akim  cwd    DIR    1,4       480 8605949500 /Users/akim/src/lrde/vcsn/experiment/sanitizer
Python  71114 akim  txt    REG    1,4     12988 8591019542 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/Resources/Python.app/Contents/MacOS/Python
Python  71114 akim  txt    REG    1,4   3013168 8604479549 /opt/local/libexec/llvm-4.0/lib/clang/4.0.1/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
Python  71114 akim  txt    REG    1,4   2643240 8591012758 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/Python
Python  71114 akim  txt    REG    1,4    107524 8590943656 /opt/local/lib/libintl.8.dylib
Python  71114 akim  txt    REG    1,4   2097528 8590888556 /opt/local/lib/libiconv.2.dylib
Python  71114 akim  txt    REG    1,4     20224 8591016920 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/lib-dynload/_heapq.cpython-35m-darwin.so
Python  71114 akim  txt    REG    1,4    326996 8591375651 /opt/local/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/site-packages/readline/gnureadline.cpython-35m-darwin.so
Python  71114 akim  txt    REG    1,4    603008 8605907803 /opt/local/lib/libncurses.6.dylib
Python  71114 akim  txt    REG    1,4    837248 8606849556 /usr/lib/dyld
Python  71114 akim    0u   CHR   16,0 0t2781894        667 /dev/ttys000
Python  71114 akim    1u   CHR   16,0 0t2781894        667 /dev/ttys000
Python  71114 akim    2u   CHR   16,0 0t2781894        667 /dev/ttys000

\o/ либасан есть! Итак, видимо, первый Python звонит второму, а DYLD_INSERT_LIBRARIES не переадресовывается.

В настоящее время я понятия не имею, почему существует два Python. Похоже, это не относится к MacPorts, как и к Python от Apple.

$ cd /System/Library/Frameworks/Python.framework/Versions/2.7
$ ls -l bin/python*
lrwxr-xr-x  1 root  wheel      7 10 déc 08:17 bin/python -> python2
lrwxr-xr-x  1 root  wheel     14 10 déc 08:17 bin/python-config -> python2-config
lrwxr-xr-x  1 root  wheel      9 10 déc 08:17 bin/python2 -> python2.7
lrwxr-xr-x  1 root  wheel     16 10 déc 08:17 bin/python2-config -> python2.7-config
-rwxr-xr-x  1 root  wheel  43104  1 déc 21:42 bin/python2.7
-rwxr-xr-x  1 root  wheel   1818 16 jul 02:20 bin/python2.7-config
lrwxr-xr-x  1 root  wheel      8 10 déc 08:17 bin/pythonw -> pythonw2
lrwxr-xr-x  1 root  wheel     10 10 déc 08:17 bin/pythonw2 -> pythonw2.7
-rwxr-xr-x  1 root  wheel  43104  1 déc 21:42 bin/pythonw2.7
$ ls -l Resources/Python.app/Contents/MacOS/Python
-rwxr-xr-x  1 root  wheel  51744  1 déc 21:48 Resources/Python.app/Contents/MacOS/Python
person akim    schedule 17.12.2017
comment
это имеет так много смысла в ретроспективе! отличный детектив, сэр! есть голосование. - person Brad Allred; 18.12.2017

Это может быть далеко не идеальный ответ, но он должен предоставить вам то, что вам нужно.

Я предлагаю вам сделать проект Xcode для сборки всего (да, я знаю, что вы хотите использовать make, это будет позже)

Предполагая, что вы уже знаете, как получить проект Xcode, строящий все, я пропущу и включу Address Sanitizer:

Product -> Scheme -> Edit Scheme откроет это окно: Редактировать схему

Установите флажки для Address Sanitizer. Вам нужно сделать это только для вашего основного приложения. По моему опыту, все зависимости будут построены соответствующим образом.

Далее Создайте основное целевое приложение. Нам нужно будет изучить вызовы компилятора и компоновщика, чтобы мы могли скопировать все необходимые флаги/шаги в файлы make.

Вывод сборки

Я обвел 2 соответствующие кнопки, чтобы получить вывод. Крайняя правая кнопка раскрывает вызов компилятора, используемый для сборки.

соответствующий вывод

На этом снимке показана как кнопка сохранения (ее может быть проще изучить в другой программе), так и некоторые соответствующие флаги, используемые для сборки. Вам нужно будет изучить выходные данные для всех целей (зависимостей), чтобы получить четкое представление.

Надеюсь это поможет. FWIW проект в моем примере имеет собственный модуль Python, написанный на C ++ (непосредственно с использованием libPython, а не Boost), поэтому я знаю, что вы можете использовать Asan на тех, кто даже использует фреймворки Python, поставляемые системой.

person Brad Allred    schedule 09.12.2017
comment
Привет, Брэд. Я пытался добавить -D_LIBCPP_HAS_NO_ASAN к MWE в своем вопросе, но безрезультатно. Это все еще не работает. - person akim; 13.12.2017
comment
@akim Я не хотел сказать, что это все, чего тебе не хватало. это усеченный вывод. Я имел в виду, что вы должны изучить результат вашего собственного проекта. Это не принесет вам никакой пользы, если у вас нет собственного проекта Xcode, который работает с Asan. Сначала сделайте это, остальное должно быть легко. - person Brad Allred; 13.12.2017
comment
Итак, я создал проект Xcode (это заняло у меня довольно много времени, так как я его не использую) и включил asan и т. д. И результат точно такой же: я не могу загрузить этот модуль в Python, потому что ASAN загружается слишком поздно. . - person akim; 14.12.2017
comment
@akim Я не могу комментировать ваш проект Xcode (если только он не находится на github или что-то в этом роде), но вы пытались статически связать Asan? например, -static-libasan, если он скомпилирован в двоичный файл, он должен обойти эту проблему. - person Brad Allred; 14.12.2017
comment
Нет такой опции с большим количеством Clangs. Как задокументировано: статическая привязка не поддерживается. - person akim; 14.12.2017
comment
@akim не поддерживается, не значит, что не работает. Я все время статически связываюсь с asan, когда строю с помощью GCC: /а> - person Brad Allred; 14.12.2017
comment
Я имею в виду, что я выполнил команду, и clang сказал мне clang: error: unknown argument: '-static-libasan'. Команда была: clang++-mp-4.0 -isystem/opt/local/include $(python3.5-config --includes) -g -std=c++14 -fsanitize=address -fno-omit-frame-pointer -L/opt/local/lib -shared -o hello_ext.so hello_ext.cc -lboost_python3-mt $(python3.5-config --ldflags) -static-libasan. И GCC для меня не вариант: мой системный компилятор Clang, а не GCC. - person akim; 14.12.2017
comment
И извините за опечатку выше, вместо Нет такой опции с большим количеством Clangs читайте Нет такой опции с современными Clangs. - person akim; 14.12.2017
comment
@akim Мне жаль, похоже, ты потратил свое время на мое предложение. Когда вы выясните это, напишите ответ, и я, скорее всего, удалю свой. До тех пор это все еще может оказаться полезным для других в других контекстах. - person Brad Allred; 15.12.2017
comment
Привет, Брэд. Извините, если я так прозвучала, это не было моим намерением. Я очень ценю, что вы нашли время, чтобы научить меня своему подходу. - person akim; 17.12.2017
comment
@akim добро пожаловать. Поскольку этот ответ получил положительные отзывы, я оставлю его, чтобы он мог оказаться полезным для других в будущем. Рад, что вы нашли ответ. - person Brad Allred; 18.12.2017