пятница, июля 13, 2007

Безопасность в С++

C++: A Cautionary Tale, or, 1 Hour Of Your Black Hat Trip is Spoken For - Томас Пташек рассказывает почему язык С++ небезопасен. Критикует следующие моменты.

Исключения. Есть такая точка зрения, что исключения есть зло. По этому поводу еще как-то Джоэл Спольски высказывался. Пташек рассказывает о следующем: после выброса исключения останавливается выполнение всех функций по цепочке до места выброса исключения. И в этих функциях могут остаться невалидные уже указатели.

delete. В С++ есть выражение delete и есть delete[]. И их очень легко перепутать. У меня есть старый пост про работу delete'ов Что делает выражение вида delete p. Пташек дает ссылку на Attacking delete and delete [] in C++, где гораздо подробнее рассказывается о работе delete'ов, со схемами использования ошибок программистов для всяких злобных дел.

STL. Итераторы, например, вектора становятся невалидными после изменения этого вектора.

Это не все, у Пташека написано больше и подробнее. Все эти проблемы решаются, все знают как их решать. Чтобы не было неприятностей с исключениями есть RAII, потом нужно вызывать правильный вариант delete и быть повнимательнее с итераторами... Но люди, они ошибаются.

Цитата из поста Пташека:
"Я - специалист по безопасности, которому платят, чтобы проискивать исходники самых крупных и известных C++ продуктов и, проблема в том, что я продолжаю находить ошибки. Просто знать как надо делать недостаточно."

15 коммент.:

Raider комментирует...

Все это немного притянуто за уши. Можно очень сильно сократить негатив от этих моментов. Достаточно пользоваться простыми правилами (мы используем подобные в наших проектах):

1. все операции с памятью - через умные указатели (std::auto_ptr, boost::shared_ptr, boost::scoped_ptr, scoped_array, std::vector, Loki::SmartPtr). тогда про delete вспоминать и не придется. а если не нашелся подходящий умный указатель - напиши его и локализуй в нем все эти delete.

2. все использование ресурсов - через RAII (в частности, для простых случаев очень подходит Loki::ScopeGuard)

Kirill Belokurov комментирует...

В целом пост Пташека можно резюмировать как "C++ позволяет вам выстрелить себе в ногу". Пташек перечисляет 3 достаточно известные потенциальные ошибки, но - ничего нового по сути. SNR поста очень низкий.

А то что "люди ошибаются" - ну так люди всегда ошибаются, вне зависимости от того, насколько idiot-proof язык/инструмент им дать.

Alena комментирует...

В целом пост Пташека можно резюмировать как "C++ позволяет вам выстрелить себе в ногу"

Хех, ну себе-то еще ничего. Но тут объясняется, что другие могут использовать эти ошибки с выгодой для себя.

Анонимный комментирует...

Вопрос не не в том, похож ли C++ на опасную бритву без ручки, вопрос в том, почему им продолжают пользоваться. Для любителей безопасной езды есть ява, есть си-шарп.

Kirill Belokurov комментирует...

Но тут объясняется, что другие могут использовать эти ошибки с выгодой для себя.

:) Это справедливо для любой более-менее серъёзной ошибки.


Кстати, такое впечатление, что пост is kind of rant. Или самореклама (см. point 1) :)

Вообще, начиная с "the notion that C++ is a more secure language than C is a myth" всё уже плохо пахнет. :)

archimed7592 комментирует...

Вопрос не не в том, похож ли C++ на опасную бритву без ручки, вопрос в том, почему им продолжают пользоваться. Для любителей безопасной езды есть ява, есть си-шарп.
Они исключают только один пункт - управление памятью.
В остальном(управление ресурсами, те же итераторы и т.д.) они ничем не лучше.

И ни один язык не выпрямит кривые руки.

lrrr комментирует...

Они исключают только один пункт - управление памятью.
Это на первый взгляд такая мелочь :)
Реально основной плюс -- это размещение всех объектов на хипе (boxed values) и отсутствие низкоуровневых указателей (=>нормальная безопасная система типов), а к этому уже как логичное дополнение -- сборка мусора.

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

lrrr комментирует...

Насчет же "кривых рук" -- это разумеется.

Но если исходить из того что "плохие" программисты все же присутствуют в команде, то у них остается меньше возможностей делать трудновылавливаемые баги, и это замечательно.

archimed7592 комментирует...

Реально основной плюс -- это размещение всех объектов на хипе (boxed values) и отсутствие низкоуровневых указателей (=>нормальная безопасная система типов), а к этому уже как логичное дополнение -- сборка мусора.

Ну не знаю, не знаю. Кому это "реально основной" плюс, кому - нет. Кому это "нормальная" система типов, кому - нет(см. D&E про эту "нормальную" систему типов).

ISO/IEC 14883:2009 будет поддерживать GC как опциональную возможность языка. Точнее говоря, она будет обязательной, но ею можно будет не пользоваться(так же, как исключениями, например) и не получать падения производительности просто из-за факта, что эта возможность есть в языке(опять же, см. D&E про zero-overhead principle). Лично я, даже после этого нововведения в язык, использовать GC-MM не буду. По мне, эта технология - излишество, причём очень дорогое: возможностей RAII вполне хватает для полноценного контроля ситуации.

Анонимный комментирует...

По поводу exceptions - Джоэль забыл добавить один чрезвычайно важный факт. Код с exceptions получается хуже (и по памяти, и по быстродействию) на десятки процентов, а то и в разы. По-крайней мере, это верно для GCC - посмотрите в результирующем ассемблере, как компилятор строит function frame. А теперь, перекомпилируйте с -fno-exceptions и посмотрите ещё раз...

Так что, как бы не был красив, логичен и лаконичен код без exceptions - в сад. По крайней мере, до тех пор, пока их кто-нибудь нормально не имплементирует.

archimed7592 комментирует...

Вообще то я про D&E - Design & Evolution, Страуструп.

Alena комментирует...

2archimed7592
ISO/IEC 14883:2009 будет поддерживать GC как опциональную возможность языка.
...
По мне, эта технология - излишество, причём очень дорогое: возможностей RAII вполне хватает для полноценного контроля ситуации.


Автоматическая сборка мусора решает две проблемы из перечисленных Пташеком. Причем без участия программиста, что исключает возможность ошибиться. Я никак не могу назвать ее излишеством.

2rsx11
Так что, как бы не был красив, логичен и лаконичен код без exceptions - в сад. По крайней мере, до тех пор, пока их кто-нибудь нормально не имплементирует.

Эээ... Возможно, имелся в виду код c исключениями? А то фраза теряет смысл.

lrrr комментирует...

Ну не знаю, не знаю. Кому это "реально основной" плюс, кому - нет. Кому это "нормальная" система типов, кому - нет.
Ну я той точки зрения, что это все помогает сделать язык "более высокоуровневым", а систему типов более выразительной и стройной.

Когда нужно написать супербыстрый низкоуровневый код, это все может мешать, да.

Предлагаемая в C++09 сборка мусора, согласен -- во многом как пятое колесо выглядит. Чтоб было органично -- нужно ее сделать обязательной, выкинуть поинтеры и выкинуть RAII (и получится C# :).

Про C++, GC и хип я чуть подробнее тут и тут мысль развернул, если интересно.

Анонимный комментирует...

Так что, как бы не был красив, логичен и лаконичен код без exceptions - в сад. По крайней мере, до тех пор, пока их кто-нибудь нормально не имплементирует.

Эээ... Возможно, имелся в виду код c исключениями? А то фраза теряет смысл.


Да, конечнo, имeнно с исключениями, sorry.

Анонимный комментирует...

archimed7592
Лично я, даже после этого нововведения в язык, использовать GC-MM не буду. По мне, эта технология - излишество, причём очень дорогое: возможностей RAII вполне хватает для полноценного контроля ситуации.
Правильно :-) RAII позволяет контролировать ресурсы вообще, а не только лишь память. Все эти джавы со своими GC идут лесом, как только дело доходит до контроля за ресурсами, ибо написание exception-safe кода без RAII превращается в сущую муку (не говоря уже о том, чтобы потом этот код читать и сопровождать).