The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]



"Обновление Firefox 74.0.1 с устранением 0-day уязвимостей"
Версия для распечатки Пред. тема | След. тема
Форум Разговоры, обсуждение новостей
Исходное сообщение [ Отслеживать ]
Присылайте удачные настройки в раздел примеров файлов конфигурации на WIKI.opennet.ru.
. "Обновление Firefox 74.0.1 с устранением 0-day уязвимостей" +3 +/
Сообщение от Ordu (ok), 05-Апр-20, 02:05 
> Так это и не баг вовсе.

Что значит "не баг"? С точки зрения языка это может и не баг, а фича, а с точки зрения программы переполнение может привести к тому, что твой индекс в массив окажется отрицательным, и когда ты проверишь что i<len, то получишь true, но когда пройдёшь по индексу окажешься за границами массива. То есть это, всё же, баг.

> А какие ещё могут быть варианты?

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

1. Дать простые способы считать с проверкой на переполнение. То есть не какие-то кодовые высказывания языка, которые позволяют проверить на переполнение не наступив ни на какие грабли, не какое-то сакральное знание, требующее долгого обучения, которое не исключает высокой когнитивной нагрузки в процессе написания кода, а _простые_ способы. Это можно сделать либо на уровне библиотеки (например, определив тип checked_int, для которого будут определены все арифметические операции, но переполнение при их выполнении будет выкидывать исключение; или хотя бы функции checked_add, checked_sub, checked_mult, checked_div, которые будут делать то же самое, но над обычными int'ами), либо на уровне языка (например, дав возможность пометить блок кода для компилятора, чтобы тот всю арифметику в нём компилировал бы с проверкой на переполнение). Вариант с компилятором удачнее, так как он позволяет в компилятор вставить оптимизации, специально заточенные на арифметику с отловом переполнений. Скажем, компилятор сможет переупорядочивать операции, и может быть какие-то из проверок выкидывать.

2. Проверка индексов в рантайме. Это не так страшно, если включить агрессивный инлайн кода, и хороший оптимизатор, который сможет проверки выкидывать из циклов наружу, а когда не сможет то хотя бы за счёт спекулятивного выполнения сведёт их стоимость к ~нулю. Это не спасёт от переполнений целых в общем случае, но от довольно распространённых последствий этих переполнений спасёт.

3. Замена индексов итераторами и функционалами. Когда вместо for(i = 0; i < len; i++) sum += array[i]; ты пишешь sum = array.fold(sum, |acc, x| { return acc + x }), у тебя нет ну никаких шансов выйти за границы массива. Такой функциональный стиль не всегда удобен, но когда он удобен он устраняет не только возможность выхода за границы массивов, он ещё и гарантирует отсутствие ненужных проверок в рантайме (даже при -O0), а в некоторых случаях он позволяет оптимизировать код лучше, потому как этот стиль подталкивает программиста писать код, который хорошо векторизуется.

И, мне кажется, (1) наиболее важный пункт: постоянно обдумывать каждую операцию на предмет того, что может случится в случае переполнения, и может ли -- это очень утомляет. Утомление ослабляет внимание, а ослабленное внимание приводит к багам. Если же есть простые способы, которые не отвлекают от основной задачи, и всё что мне надо -- не забыть сказать компилятору, что тут очень важно чтобы не было переполнения, то всё становится резко проще. А если при этом ещё добавить строгую типизацию, и потребовать чтобы вся индексы имели бы тип off_t или size_t, и никакие int'ы не приводились бы к size_t и off_t молчаливо, а потом к size_t и off_t приделать обязательную проверку на переполнение, выполняемую компилятором, то тогда можно довести ситуацию, когда программисту проще писать с проверкой на переполнение, чем без неё, а это значит, что изворачиваться и уходить от этой проверки он будет только тогда, когда это действительно важно. То есть довольно редко, а если редко это делать, то и баги такие будут возникать редко.

> Надо просто правильно выбирать типы данных

Что ты имеешь в виду? off_t -- это знаковый тип, и тому есть причины. Его не удастся сделать беззнаковым, чтобы проверять на выход только за одну границу. Ты не сможешь сделать его больше. Точнее сможешь, но это бессмысленно, потому как перед применением всё равно придётся обрезать лишние биты. Да и дорого это: size_t и off_t, как правило размером с машинное слово, если ты сделаешь их больше, то вся арифметика станет резко дороже -- проще уж автоматически проверок на выход за границы в рантайме навтыкать.

Или ты к тому, что под количество денег в игре надо выделять не 32 бита, а целое произвольной точности, чтобы у игрока не было бы возможности, нырнув в глубокий минус, вынырнуть владельцем игровой вселенной? Может быть так и нужно делать. Но не всегда разумно использовать целое произвольной точности. А если просто взять 64 бита вместо 32, то это не обязательно спасёт: произведение двух 32 битных целых может опасно подобраться к переполнению 64 битного, то есть какой-нибудь промежуточный результат вычислений может переполнить и 64 бита, и... То есть это всё полумеры, если тебе нужна защита от переполнений, то нужно проверять на переполнения, то есть вставлять рантайм-проверки. И либо ты будешь делать это ручками, либо научишь компилятор делать это за тебя.

Ответить | Правка | Наверх | Cообщить модератору

Оглавление
Обновление Firefox 74.0.1 с устранением 0-day уязвимостей, opennews, 03-Апр-20, 22:45  [смотреть все]
Форумы | Темы | Пред. тема | След. тема



Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру