Опубликован релиз проекта Memsafe, реализующего механизм безопасной работы со ссылочными типами и динамической памятью в коде на языке С++. Защита может быть добавлена без нарушения обратной совместимости со старым С++ кодом. Проект оформлен в виде одного заголовочного файла memsafe.h и плагина для компилятора Clang. Код распространяется под лицензией LGPL 2.1.
Для безопасной работы с памятью использована концепция защиты из языка NewLang, адаптированная для С++ и дополненная контролем инвалидации ссылочных типов. В основе безопасного управления динамической памятью лежит использование сильных и слабых указателей, а также контроль времени жизни копий переменных с сильными указателями во время компиляции исходного кода программы.
Предложенная техника защиты походит на концепцию владения и заимствования из языка Rust, но реализована на базе сильных и слабых ссылок (стандартных механизмов С++ shared_ptr и weak_ptr). Любые операции с данными для переменной по ссылке возможны только после её захвата, т.е. после преобразования слабой ссылки (weak_ptr) в сильную (shared_ptr).
Способ маркировки объектов в коде библиотеки реализован с помощью С++ атрибутов “[[memsafe(…)]]”, что напоминает способ из спецификаций P3038 и P3081 с профилями безопасности (предложены Бьёрном Страуструпом (Bjarne Stroustrup) и Гербом Саттером (Herb Sutter)), но не требует разработки нового стандарта С++ (достаточно использовать уже существующий С++20).
Пример работы плагина анализатора для кода:
std::vector vec(100000, 0); auto x = vec.begin(); auto y = vec.end(); vec = {}; vec.shrink_to_fit(); std::sort(x, y); // malloc(): unaligned tcache chunk detected or Segmentation fault
Командная строка для запуска компилятора с плагином:
clang++ -std=c++20 -Xclang -load -Xclang ./memsafe_clang.so -Xclang -add-plugin -Xclang memsafe _example.cpp
Фрагмент вывода плагина компилятора с сообщениями об ошибках, связанных с недействительностью ссылочных переменных после изменения данных в основной переменной:
_example.cpp:29:17: warning: using main variable ‘vect’ 29 | vect = {}; | ^ _example.cpp:30:17: warning: using main variable ‘vect’ 30 | vect.shrink_to_fit(); | ^ _example.cpp:31:27: error: Using the dependent variable ‘beg’ after changing the main variable ‘vect’! 31 | std::sort(beg, vect.end()); // malloc(): unaligned tcache chunk detected or Segmentation fault | ^
Уровень сообщений плагина можно ограничить в коде с помощью макроса или аргументом командной строки. После проверки исходного кода, плагин можно вообще не использовать, так как он только анализирует AST, но не вносит в него никаких исправлений. С использованием пространства имён “memsafe” и разделением защищённого и незащищённого (unsafe) кода можно познакомиться в данном примере.