Здравствуйте, gandjustas, Вы писали:
V>>Хех, даже ретрограды в гугле, посчитав, что у них "around 70% of our serious security bugs are memory safety problems", начинают наконец-то что-то подозревать "we feel it may be necessary to ban raw pointers from C++" V>>https://www.chromium.org/Home/chromium-security/memory-safety
G>35%, половина из memory-related багов — это use after free. Умные указатели от неё не спасают.
Здравствуйте, gandjustas, Вы писали:
G>35%, половина из memory-related багов — это use after free. Умные указатели от неё не спасают.
В какой-то степени могут спасти, например когда reset для unique_ptr вызван, но сам unique_ptr еще не разрушен, тогда получим обращение по нулевому указателю. Можно правда обратиться к уже разрушенному unique_ptr по висячей ссылке, но это уже несколько другая проблема.
G>Собственно в статье об этом и написано, бан голых указателей далеко не первый пункт в предлагаемых решениях.
Да вроде как раз о вероятной возможности бана raw указателей и пишут.
"We expect this strategy will boil down to two major strands:
— Significant changes to the C++ developer experience, with some performance impact. (For instance, no raw pointers, bounds checks, and garbage collection.)
— An option of a programming language designed for compile-time safety checks with less runtime performance impact — but obviously there is a cost to bridge between C++ and that new language."
Приоритеты там явно не расставлены, поэтому сложно сказать какой пункт первый, а какой нет.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, gandjustas, Вы писали:
V>>>Хех, даже ретрограды в гугле, посчитав, что у них "around 70% of our serious security bugs are memory safety problems", начинают наконец-то что-то подозревать "we feel it may be necessary to ban raw pointers from C++" V>>>https://www.chromium.org/Home/chromium-security/memory-safety
G>>35%, половина из memory-related багов — это use after free. Умные указатели от неё не спасают. G>>Вернее так: спасение от use-after-free это GC и/или подсчёт ссылок, что сильно бьет по производительности c-style copy-free кода.
Верну ту часть сообщения, которую вы зачем-то вырезали.
M>Хм. А можно узнать, почему не спасают?
Типичная ситуация:
1) Объект 1 имеет non-owning pointer на объект 2. В текущей версии языка non-owning pointer рекомендуется выражать обычным C-pointer
2) Объект 2 разрушается раньше чем объект 1. Причем эту ситуацию очень легко получить, просто переставив порядок объявлений в классе.
Как эту ситуацию можно решить:
1) Вместо C-pointer передавать везде weak_ptr, а еще в половине методов вписать проверку этих слабых указателей. Но это означает что почти все owning pointer надо сделать shared_ptr и получить большой оверхед на подсчет ссылок.
2) Воспользоваться GC, а-ля https://github.com/hsutter/gcpp, тогда моно механически все указатели поменять на deferred_ptr и повтыкать вызовы collect в дексрукторах объектов верхнего уровня. Но это тоже оверхед, а еще конфликтует с move-семантикой в текущем виде.
Я пишу на С++ плагин к одной одной игрушке, уделяя этому по 2-4 часа в день и дважды за неделю столкнулся с use-after-free, хотя у меня вообще ни одного оператора new нет (вру, один есть и проблема не в нем) и . Потому что use-after-free можно получить с указателями, полученными от OC или COM, особенно когда код асинхронный.
Здравствуйте, Voivoid, Вы писали:
V>Здравствуйте, gandjustas, Вы писали:
G>>35%, половина из memory-related багов — это use after free. Умные указатели от неё не спасают. V>В какой-то степени могут спасти, например когда reset для unique_ptr вызван, но сам unique_ptr еще не разрушен, тогда получим обращение по нулевому указателю. Можно правда обратиться к уже разрушенному unique_ptr по висячей ссылке, но это уже несколько другая проблема.
Это та самая проблема, от которой страдают. Потому что парность new\delete, malloc\free легко отследить на уровне владельца объекта даже без помощи умных указателей. А когда указатель передается куда-то еще, особенно в асинхронный код, то начинается жопа.
Здравствуйте, gandjustas, Вы писали:
G>Как эту ситуацию можно решить:
G>1) Вместо C-pointer передавать везде weak_ptr, а еще в половине методов вписать проверку этих слабых указателей. Но это означает что почти все owning pointer надо сделать shared_ptr и получить большой оверхед на подсчет ссылок.
Что, такой уж большой оверхед? Его кто-нибудь измерял?
G>Типичная ситуация: G>1) Объект 1 имеет non-owning pointer на объект 2. В текущей версии языка non-owning pointer рекомендуется выражать обычным C-pointer G>2) Объект 2 разрушается раньше чем объект 1. Причем эту ситуацию очень легко получить, просто переставив порядок объявлений в классе.
В текущей инкарнации языка как раз таки не рекомендуется иметь non-owning pointer на объект 2, если ты не можешь гарантировать, что объект 1 разрушится раньше объекта 2. Такие вещи как раз принято выражать с помощью shared_ptr/weak_ptr.
G>Как эту ситуацию можно решить:
G>1) Вместо C-pointer передавать везде weak_ptr, а еще в половине методов вписать проверку этих слабых указателей. Но это означает что почти все owning pointer надо сделать shared_ptr и получить большой оверхед на подсчет ссылок. G>2) Воспользоваться GC, а-ля https://github.com/hsutter/gcpp, тогда моно механически все указатели поменять на deferred_ptr и повтыкать вызовы collect в дексрукторах объектов верхнего уровня. Но это тоже оверхед, а еще конфликтует с move-семантикой в текущем виде.
Нужно просто проектировать иерархию объектов с учетом времени жизни объектов и ownership. Скажем, что-то такое, это вполне ОК и без умных указателей:
Здесь объект Workflow разрушится раньше чем s1 и s2, поэтому он может иметь указатели на них. Но если бы все поля класса Foo создавались где-то еще, а не как поля объекта к которым применим принцип deterministic destruction, я бы сделал их умными указателями.
G>Я пишу на С++ плагин к одной одной игрушке, уделяя этому по 2-4 часа в день и дважды за неделю столкнулся с use-after-free, хотя у меня вообще ни одного оператора new нет (вру, один есть и проблема не в нем) и . Потому что use-after-free можно получить с указателями, полученными от OC или COM, особенно когда код асинхронный.
я вот честно говоря не вспомню когда последний раз у меня был такой баг, но я стараюсь проектировать приложения так, чтобы ownership был явным, иерархическим, без циклический зависимостей и тд, это же работает и с асинхронщиной прекрасно, если вспомнить, что lifetime твоих асинхронных обработчиков сообщений привязан к I/O объекту (boost::asio::io_service например, или какому-нибудь сокету или пайпу). В принципе, в boost asio идиоматично пихать в хэндлеры умные указатели.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Здравствуйте, gandjustas, Вы писали:
G>>Типичная ситуация: G>>1) Объект 1 имеет non-owning pointer на объект 2. В текущей версии языка non-owning pointer рекомендуется выражать обычным C-pointer G>>2) Объект 2 разрушается раньше чем объект 1. Причем эту ситуацию очень легко получить, просто переставив порядок объявлений в классе.
CK>В текущей инкарнации языка как раз таки не рекомендуется иметь non-owning pointer на объект 2, если ты не можешь гарантировать, что объект 1 разрушится раньше объекта 2. Такие вещи как раз принято выражать с помощью shared_ptr/weak_ptr.
Но мы же понимаем, что далеко не весь код так написан, как по причинам быстродействия, так и по историческим.
G>>Как эту ситуацию можно решить:
G>>1) Вместо C-pointer передавать везде weak_ptr, а еще в половине методов вписать проверку этих слабых указателей. Но это означает что почти все owning pointer надо сделать shared_ptr и получить большой оверхед на подсчет ссылок. G>>2) Воспользоваться GC, а-ля https://github.com/hsutter/gcpp, тогда моно механически все указатели поменять на deferred_ptr и повтыкать вызовы collect в дексрукторах объектов верхнего уровня. Но это тоже оверхед, а еще конфликтует с move-семантикой в текущем виде.
CK>Нужно просто проектировать иерархию объектов с учетом времени жизни объектов и ownership.
Какбы в проекте старше 5 лет это сложно сделать. Особенно если код асинхронный.
CK>Скажем, что-то такое, это вполне ОК и без умных указателей: CK>
CK>Здесь объект Workflow разрушится раньше чем s1 и s2, поэтому он может иметь указатели на них. Но если бы все поля класса Foo создавались где-то еще, а не как поля объекта к которым применим принцип deterministic destruction, я бы сделал их умными указателями.
Но ошибку довольно легко допустить если поменять порядок объявлений в классе. Компилятор никак не подскажет, умные указатели не помогут.
G>>Я пишу на С++ плагин к одной одной игрушке, уделяя этому по 2-4 часа в день и дважды за неделю столкнулся с use-after-free, хотя у меня вообще ни одного оператора new нет (вру, один есть и проблема не в нем) и . Потому что use-after-free можно получить с указателями, полученными от OC или COM, особенно когда код асинхронный.
CK>я вот честно говоря не вспомню когда последний раз у меня был такой баг, но я стараюсь проектировать приложения так, чтобы ownership был явным, иерархическим, без циклический зависимостей и тд, это же работает и с асинхронщиной прекрасно, если вспомнить, что lifetime твоих асинхронных обработчиков сообщений привязан к I/O объекту (boost::asio::io_service например, или какому-нибудь сокету или пайпу). В принципе, в boost asio идиоматично пихать в хэндлеры умные указатели.
далеко не все используют буст. Я так понимаю что вышупомянутый хром как раз не использует.
Здравствуйте, gandjustas, Вы писали:
G>Это та самая проблема, от которой страдают. Потому что парность new\delete, malloc\free легко отследить на уровне владельца объекта даже без помощи умных указателей. А когда указатель передается куда-то еще, особенно в асинхронный код, то начинается жопа.
Здравствуйте, chaotic-kotik, Вы писали:
CK>В текущей инкарнации языка как раз таки не рекомендуется иметь non-owning pointer на объект 2, если ты не можешь гарантировать, что объект 1 разрушится раньше объекта 2. Такие вещи как раз принято выражать с помощью shared_ptr/weak_ptr.
Где можно прочитать такие рекомендации? Голые владеющие указатели -- да, плохо.
А вот голые невладеющие указатели/ссылки где не рекомендуют использовать?
Ведь weak_ptr -- это не бесплатно. Да и weak_ptr идет в придачу к shared_ptr, а далеко не все объекты создаются через shared_ptr (или для них выгодно создавать shared_ptr).
CK>>Здесь объект Workflow разрушится раньше чем s1 и s2, поэтому он может иметь указатели на них. Но если бы все поля класса Foo создавались где-то еще, а не как поля объекта к которым применим принцип deterministic destruction, я бы сделал их умными указателями. G>Но ошибку довольно легко допустить если поменять порядок объявлений в классе. Компилятор никак не подскажет, умные указатели не помогут.
Можно инициализировать workflow в списке инициализации в конструкторе. Тогда компилятор подскажет. Без этого, достаточно сделать так, чтобы деструктор Workflow не дергал переданные в него указатели в своем деструкторе.
CK>>я вот честно говоря не вспомню когда последний раз у меня был такой баг, но я стараюсь проектировать приложения так, чтобы ownership был явным, иерархическим, без циклический зависимостей и тд, это же работает и с асинхронщиной прекрасно, если вспомнить, что lifetime твоих асинхронных обработчиков сообщений привязан к I/O объекту (boost::asio::io_service например, или какому-нибудь сокету или пайпу). В принципе, в boost asio идиоматично пихать в хэндлеры умные указатели. G>далеко не все используют буст. Я так понимаю что вышупомянутый хром как раз не использует.
Тут дело не в бусте, а в принципе. В асинхронном коде у тебя все равно есть объект, который дергает асинхронно хэндлеры, скажем это thread pool какой-нибудь. Нам нужно обеспечить одну гарантию, чтобы в случае остановки нашего thread pool все находящиеся в очереди хэндлеры были вызваны с кодом ошибки (напирмер ERR_CANCELED), ну или альтернативно — чтобы они все были удалены. Дальше я уже могу сделать так, чтобы время жизни объектов, с которыми работают мои хэндлеры было связано либо с временем жизни thread pool-a, либо самого хэндлера.
Пример: хэндлер использует объект Cache, общий для всех хэндлеров, при этом Cache и ThreadPool являются членами одного класса. При этом хэндлер может иметь умные указатели на per-request данные, которые почистятся если thread pool остановят.
В общем, я не говорю что это панацея и не хочу никого поучать, но явно продумывать ownership и lifetimes мне всегда очень помогало.
Здравствуйте, so5team, Вы писали:
S>Где можно прочитать такие рекомендации? Голые владеющие указатели -- да, плохо. S>А вот голые невладеющие указатели/ссылки где не рекомендуют использовать?
не использовать, а хранить, делать членами класса и тд
просто передать невладеющий указатель в ф-ю, которая будет его использовать пока работает, это норм
передавать его в конструктор объекта и хранить в одном из полей — не норм
S>Ведь weak_ptr -- это не бесплатно. Да и weak_ptr идет в придачу к shared_ptr, а далеко не все объекты создаются через shared_ptr (или для них выгодно создавать shared_ptr).
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, gandjustas, Вы писали:
G>>Это та самая проблема, от которой страдают. Потому что парность new\delete, malloc\free легко отследить на уровне владельца объекта даже без помощи умных указателей. А когда указатель передается куда-то еще, особенно в асинхронный код, то начинается жопа.
M>И тут выручают умные указатели
Конечно выручают, путем оверхеда на кадое присванивание\передачу параметром. А также утечки памяти при не очень удачных замыканиях. Не говоря уже о том, что с помощью лябд легко сделать циклические ссылки.
Здравствуйте, chaotic-kotik, Вы писали:
CK>не использовать, а хранить, делать членами класса и тд CK>просто передать невладеющий указатель в ф-ю, которая будет его использовать пока работает, это норм CK>передавать его в конструктор объекта и хранить в одном из полей — не норм
Пусть в такой формулировке. Где подобные рекомендации можно прочитать?
S>>Ведь weak_ptr -- это не бесплатно. Да и weak_ptr идет в придачу к shared_ptr, а далеко не все объекты создаются через shared_ptr (или для них выгодно создавать shared_ptr).
CK>конечно не бесплатно (не утверждал обратное)
Поскольку не бесплатно, то значит это не будут использовать по вполне объективным причинам.
Здравствуйте, chaotic-kotik, Вы писали:
CK>Тут дело не в бусте, а в принципе. В асинхронном коде у тебя все равно есть объект, который дергает асинхронно хэндлеры, скажем это thread pool какой-нибудь. Нам нужно обеспечить одну гарантию, чтобы в случае остановки нашего thread pool все находящиеся в очереди хэндлеры были вызваны с кодом ошибки (напирмер ERR_CANCELED), ну или альтернативно — чтобы они все были удалены. Дальше я уже могу сделать так, чтобы время жизни объектов, с которыми работают мои хэндлеры было связано либо с временем жизни thread pool-a, либо самого хэндлера.
Не только в принципе, но и в библиотеке (в данном случае в бусте).
те же самые умные указатели существовали двано, но мало кто торопился их использовать, пока они не попали в STL.
Аналогично в STL сейчас отсуствуют абстрации, а также отсутствуют рекомендации для работы с асинхронным кодом.
CK>В общем, я не говорю что это панацея и не хочу никого поучать, но явно продумывать ownership и lifetimes мне всегда очень помогало.
Ну богатым и здоровым всегда был лучше, чем бедным и больным. Тем не менее проблемы объективно существуют и никакого штатного способа разрешения их нет.
CK>Можно инициализировать workflow в списке инициализации в конструкторе. Тогда компилятор подскажет. Без этого, достаточно сделать так, чтобы деструктор Workflow не дергал переданные в него указатели в своем деструкторе.
Можно сделат что угодно, но это означает что устройство класса Foo начинает влиять на класс Workflow. А последний вообще может быть вам недоступен для изменения.
Это кстати громадная проблема которую я заметил в C++. Я три или четыре раза переписывал один и тот же метод, потому вызывающий код менялся. логика работы оставалась, а менялся способ работы с объектом: ссылки, умные указатели итд.
Здравствуйте, gandjustas, Вы писали:
G>Это кстати громадная проблема которую я заметил в C++. Я три или четыре раза переписывал один и тот же метод, потому вызывающий код менялся. логика работы оставалась, а менялся способ работы с объектом: ссылки, умные указатели итд.
Спасение утопающих — дело рук самих утопающих. Я даже свою библиотеку с умными указателями писать начал (C++ Object Token Library
Здравствуйте, so5team, Вы писали:
S>Пусть в такой формулировке. Где подобные рекомендации можно прочитать?
наш code guide таки прямо это не рекомендует, если что-то общедоступное, то есть core guidelines, в котором емнип а) рекомендуется использовать смарт поинтеры для управления временем жизни объектов б) не рекомендует использовать не владеющие указатели/ссылки, которые являются алиасами для объектов, временем жизни которых управляют смарт-поинтеры
Здравствуйте, chaotic-kotik, Вы писали:
S>>Пусть в такой формулировке. Где подобные рекомендации можно прочитать?
CK>наш code guide таки прямо это не рекомендует
А ваш -- это чей?
CK>б) не рекомендует использовать не владеющие указатели/ссылки, которые являются алиасами для объектов, временем жизни которых управляют смарт-поинтеры
Это несколько другое, нежели:
В текущей инкарнации языка как раз таки не рекомендуется иметь non-owning pointer на объект 2, если ты не можешь гарантировать, что объект 1 разрушится раньше объекта 2.
Поскольку первоначальное ваше утверждение можно применять и к объектам, которые вообще не создаются динамически, но на которые, тем не менее, ссылаются.
Здравствуйте, so5team, Вы писали:
S>Это несколько другое, нежели: S>
В текущей инкарнации языка как раз таки не рекомендуется иметь non-owning pointer на объект 2, если ты не можешь гарантировать, что объект 1 разрушится раньше объекта 2.
S>Поскольку первоначальное ваше утверждение можно применять и к объектам, которые вообще не создаются динамически, но на которые, тем не менее, ссылаются.
не то чтобы я стремился тут дать строгое определение, но для объектов, которые не создаются динамически, я могу гарантировать требуемый lifetime
Re[4]: Верблюд - это лошадь, разработанная комитетом... :)))
Здравствуйте, YuriV, Вы писали:
Ш>>Не только языки. Хочешь угробить дело -- создай комитет. YV>Ну комитет утомил уже давно и не меня одного. У них запор сменился вербальной диареей. Все нововведения размазаны тонким слоем по трём стандартам.
Неужели народ начал что-то подозревать?
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]