[Программирование, C++, Промышленное программирование] Современный C++ нас не спасет (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Ячастокритикуюнебезопасные при работе с памятью языки, в основном C и C++, и то, как они провоцируют необычайное количество уязвимостей безопасности. Моё резюме, основанное на изучении доказательств из многочисленных крупных программных проектов на С и С++, заключается в том, что нам необходимо мигрировать нашу индустрию на безопасные для памяти языки по умолчанию (такие как Rust и Swift). Один из ответов, который я часто получаю, заключается в том, что проблема не в самих С и С++, разработчики просто неправильно их "готовят". В частности, я часто получаю в защиту C++ ответ типа: "C++ безопасен, если вы не используете унаследованную от C функциональность" [1] или аналогичный ему, что если вы используете типы и идиомы современного C++, то вы будете застрахованы от уязвимостей типа повреждения памяти, от которых страдают другие проекты.Хотелось бы отдать должное умным указателям С++, потому что они существенно помогают. К сожалению, мой опыт работы над большими С++ проектами, использующими современные идиомы, заключается в том, что этого даже близко недостаточно, чтобы остановить наплыв уязвимостей. Моя цель на оставшуюся часть этой заметки - выделить ряд абсолютно современных идиом С++, которые порождают уязвимости.Скрытая ссылка и use-after-free
#include <iostream>
#include <string>
#include <string_view>
int main() {
std::string s = "Hellooooooooooooooo ";
std::string_view sv = s + "World\n";
std::cout << sv;
}
Вот что здесь происходит, s + "World\n" создает новую строку std::string, а затем преобразует ее в std::string_view. На этом этапе временная std::string освобождается, но sv все еще указывает на память, которая ранее ей принадлежала. Любое последующее использование sv является use-after-free уязвимостью. Упс! В С++ не хватает средств, чтобы компилятор знал, что sv захватывает ссылку на что-то, где ссылка живет дольше, чем донор. Эта же проблема затрагивает std::span, также чрезвычайно современный тип С++.Другой забавный вариант включает в себя использование лямбда в С++ для сокрытия ссылки:
#include <memory>
#include <iostream>
#include <functional>
std::function<int(void)> f(std::shared_ptr<int> x) {
return [&]() { return *x; };
}
int main() {
std::function<int(void)> y(nullptr);
{
std::shared_ptr<int> x(std::make_shared<int>(4));
y = f(x);
}
std::cout << y() << std::endl;
}
Здесь [&] в f лямбда захватывает значение по ссылке. Затем в main, x выходит за пределы области видимости, уничтожая последнюю ссылку на данные и освобождая их. В этот момент y содержит висячий указатель. Это происходит, несмотря на наше тщательное использование умных указателей. И да, люди действительно пишут код, использующий std::shared_ptr&, часто как попытку избежать дополнительного приращения и уменьшения количеств в подсчитывающих ссылках.Разыменование std::optionalstd::optional представляет собой значение, которое может присутствовать, а может и не присутствовать, часто заменяя магические значения (например, -1 или nullptr). Он предлагает такие методы, как value(), которые извлекают T, которое он содержит, и вызывает исключение, если optional пуст. Однако, он также определяет operator* и operator->. Эти методы также обеспечивают доступ к хранимому T, однако они не проверяют, содержит ли optional значение или нет.Следующий код, например, просто возвращает неинициализированное значение:
#include <optional>
int f() {
std::optional<int> x(std::nullopt);
return *x;
}
Если вы используете std::optional в качестве замены nullptr, это может привести к еще более серьезным проблемам! Разыменование nullptr дает segfault (что не является проблемой безопасности, кроме как в старых ядрах). Однако, разыменование nullopt дает вам неинициализированное значение в качестве указателя, что может быть серьезной проблемой с точки зрения безопасности. Хотя T* также бывает с неинициализированным значением, это гораздо менее распространено, чем разыменование указателя, который был правильно инициализирован nullptr.И нет, это не требует использования сырых указателей. Вы можете получить неинициализированные/дикие указатели и с помощью умных указателей:
#include <optional>
#include <memory>
std::unique_ptr<int> f() {
std::optional<std::unique_ptr<int>> x(std::nullopt);
return std::move(*x);
}
Индексация std::spanstd::span обеспечивает эргономичный способ передачи ссылки на непрерывный кусок памяти вместе с длиной. Это позволяет легко писать код, который работает с несколькими различными типами; std::span может указывать на память, принадлежащую std::vector, std::array<uint8_t, N> или даже на сырой указатель. Некорректная проверка границ - частый источник уязвимостей безопасности, и во многих смыслах span помогает, гарантируя, что у вас всегда будет под рукой длина.Как и все структуры данных STL, метод span::operator[] не выполняет проверку границ. Это печально, так как operator[] является наиболее эргономичным и стандартным способом использования структур данных. std::vector и std::array можно, по крайней мере, теоретически безопасно использовать, так как они предлагают метод at(), который проверяет границы (на практике я этого никогда не видел, но можно представить себе проект, использующий инструмент статического анализа, который просто запрещает вызовы std::vector::operator[]). span не предлагает метод at(), или любой другой подобный метод, который выполняет проверку границ.Интересно, что как Firefox, так и Chromium в бэкпортах std::span выполняют проверку границ в operator[], и, следовательно, никогда не смогут безопасно мигрировать на std::span.ЗаключениеИдиомы современного C++ вводят много изменений, которые могут улучшить безопасность: умные указатели лучше выражают ожидаемое время жизни, std::span гарантирует, что у вас всегда под рукой правильная длина, std::variant обеспечивает более безопасную абстракцию для union. Однако современный C++ также вводит новые невообразимые источники уязвимостей: захват лямбд с эффектом use-after-free, неинициализированные optional и не проверяющие границы span.Мой профессиональный опыт написания относительно современного С++ кода и аудита Rust-кода (включая Rust-код, который существенно использует unsafe) заключается в том, что безопасность современного С++ просто не сравнится с языками, в который безопасностью памяти включена по умолчанию, такими как Rust и Swift (или Python и Javascript, хотя я в реальности редко встречаю программы, для который имеет смысл выбора - писать их на Python, либо на C++).Существуют значительные трудности при переносе существующих больших кодовых баз на C и C++ на другие языки - никто не может этого отрицать. Тем не менее, вопрос просто должен ставиться в том, как мы можем это сделать, а не в том, стоит ли пытаться. Даже при наличии самых современных идиом С++, очевидно, что при росте масштабов просто невозможно корректно использовать С++.[1] Это надо понимать, что речь идет о сырых указателях, массивах-как-указателях, ручном malloc/free и другом подобном функционале Си. Однако, думаю, стоит признать, что, учитывая, что Си++ явно включил в свою спецификацию Си, на практике большинство Си++-кода содержит некоторые из этих "фич".
===========
Источник:
habr.com
===========
===========
Автор оригинала: Alex Gaynor
===========Похожие новости:
- [Информационная безопасность] Conan.io – неварварские методы работы
- [Ненормальное программирование, Программирование, Go, Дизайн] Go Rant
- [Программирование, Java] Избавляемся от мусора в Java (перевод)
- [Open source, Разработка под Linux] Кроа-Хартман отверг извинения Миннесотского университета
- [Тестирование IT-систем, PHP, Программирование] Практики при работе с PHPUnit
- [Информационная безопасность] Интернет-мошенничество в регионе: почему раскрываемость низкая, а жертв все больше
- [Программирование, Разработка под Linux] Блокнот на языке Vala
- [Open source, Разработка под Linux] Разработчики Миннесотского университета опубликовали открытое письмо с извинениями сообществу Linux
- [Программирование, Delphi, Алгоритмы, HTML, PDF] Даешь свободную литературу! Или как я с политикой вуза боролся
- [Программирование, .NET, C#] Как перестать DDoS-ить чужой API и начать жить
Теги для поиска: #_programmirovanie (Программирование), #_c++, #_promyshlennoe_programmirovanie (Промышленное программирование), #_bezopasnost (безопасность), #_ujazvimosti (уязвимости), #_nadezhnoe_programmirovanie (надёжное программирование), #_programmirovanie (
Программирование
), #_c++, #_promyshlennoe_programmirovanie (
Промышленное программирование
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:20
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Ячастокритикуюнебезопасные при работе с памятью языки, в основном C и C++, и то, как они провоцируют необычайное количество уязвимостей безопасности. Моё резюме, основанное на изучении доказательств из многочисленных крупных программных проектов на С и С++, заключается в том, что нам необходимо мигрировать нашу индустрию на безопасные для памяти языки по умолчанию (такие как Rust и Swift). Один из ответов, который я часто получаю, заключается в том, что проблема не в самих С и С++, разработчики просто неправильно их "готовят". В частности, я часто получаю в защиту C++ ответ типа: "C++ безопасен, если вы не используете унаследованную от C функциональность" [1] или аналогичный ему, что если вы используете типы и идиомы современного C++, то вы будете застрахованы от уязвимостей типа повреждения памяти, от которых страдают другие проекты.Хотелось бы отдать должное умным указателям С++, потому что они существенно помогают. К сожалению, мой опыт работы над большими С++ проектами, использующими современные идиомы, заключается в том, что этого даже близко недостаточно, чтобы остановить наплыв уязвимостей. Моя цель на оставшуюся часть этой заметки - выделить ряд абсолютно современных идиом С++, которые порождают уязвимости.Скрытая ссылка и use-after-free #include <iostream>
#include <string> #include <string_view> int main() { std::string s = "Hellooooooooooooooo "; std::string_view sv = s + "World\n"; std::cout << sv; } #include <memory>
#include <iostream> #include <functional> std::function<int(void)> f(std::shared_ptr<int> x) { return [&]() { return *x; }; } int main() { std::function<int(void)> y(nullptr); { std::shared_ptr<int> x(std::make_shared<int>(4)); y = f(x); } std::cout << y() << std::endl; } #include <optional>
int f() { std::optional<int> x(std::nullopt); return *x; } #include <optional>
#include <memory> std::unique_ptr<int> f() { std::optional<std::unique_ptr<int>> x(std::nullopt); return std::move(*x); } =========== Источник: habr.com =========== =========== Автор оригинала: Alex Gaynor ===========Похожие новости:
Программирование ), #_c++, #_promyshlennoe_programmirovanie ( Промышленное программирование ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 12:20
Часовой пояс: UTC + 5