> man operating_systems
Undeadly: Умножение - небезопасная операция
В своем блоге разработчик OpenBSD рассказывает (English) об опасностях выхода за разрядную сетку при умножении в C, схожая ошибка стала причиной уязвимости OpenSSH в 2002 году, первой и единственной на данный момент удаленной уязвимости OpenBSD в стандартной установке за все время ее существования.
Roman I Khimov  в  Пятница, 31 Март 2006, 12:42  |   Комментарии: 51  |  для печати

Комментарии
Vadim Ushakov |01.04.2006 08:05
Комментарии: 78

Зарегистрирован: 18.08.2005 04:25

Радует, что среди программистов всетаки встречаются люди, умеющие программировать.

batu |04.04.2006 03:47
Комментарии: 10

Зарегистрирован: 03.07.2004 22:20

Это ошибки и транслятора и архитектуры. Что еще раз говорит о недостатках современной архитектуры компьютера.

Dron |04.04.2006 09:35
Комментарии: 558


Да, плохо то, что программисту приходится думать о том, не вылезет ли его умножение за допустимые рамки. об этом должен думать, ну если не процессор, то сама программа... и грязно ругаться... и кидаться эксепшинами... и падать в коры.
Кстати вот странно. деление на ноль есть, а умножения нет... Ж)

Но поскольку, что имеем - то и имеем...
Тот программист, который думает об этом - молодец.

fedukoff |04.04.2006 10:15
Комментарии: 178

Зарегистрирован: 24.03.2005 17:32

хмь. Я, конечно, не специалист (R), но, мне кажется, что после умножения, процессор (по крайней мере х86) устанавливает (или сбрасывает) флажок переполнения. Полуцаецо, что всеголишь одна команда ветвления после умножения и дело в шляпе.
ЗЫ моя не есть утверждать, что наличее отутствия проблемы имеет место быть, но моя есть утверждать, что присутствие наличия данной проблемы довольно легко прекратить, тють-тють слегка модернизировав компиляторное программное обеспечение.

Dron |04.04.2006 12:10
Комментарии: 558


Вообще да, если умножение вызодит за рамки типа (для ассемблера это не предел, результат может быть в два раза больше) выставляется флаг CF.

то есть одного условия достаточно чтобы вызвать какую нибудь диагностику. это верно.

Кстати я подумал, может быть это есть???
есть вот опция -mno-wide-multiply


-mno-wide-multiply
-mwide-multiply
Control whether GNU CC uses the mul and imul that produce 64 bit results in eax:edx from 32 bit operands to do long long multiplies and 32-bit division by constants.


Правда не понятно, что происходит в случае нарушения.

fedukoff |04.04.2006 12:13
Комментарии: 178

Зарегистрирован: 24.03.2005 17:32

Не попробуешь, не узнаешь!

Dron |04.04.2006 13:46
Комментарии: 558


О, точно, попробовать! Ж))

Не, не то...
унрекогнайзед опшин на i686-pc-linux-gnu

видимо это для x86-64, типа можно eax умножать, а можно rax...
но я не могу этого проверить.

В любом случае проверкой не пахнет.

Alexandoros |04.04.2006 17:13
Комментарии: 5

Зарегистрирован: 01.06.2005 10:01

size_t у гсс == int? Вот интересно какой кретин выделяет за раз память для 64к обджектов размером 65к???????? Что ж там выделять такого надо - то???? А если выделяеш масив из 200к элементов, то тут уже просто логики хватает проверить не заздоровый ли он?

fedukoff |04.04.2006 17:21
Комментарии: 178

Зарегистрирован: 24.03.2005 17:32

Я есть согласный! Если операция имеет возможность поломать чтонидь, то надо результат проверить. И результат на лицо

Dron |04.04.2006 21:22
Комментарии: 558


Здравый смысл - здравым смыслом, но всетаки нелогично...
ошибка деления - есть...
ошибки умножения - нету Ж(

даже границы массивов контролируются компиляторами...
а вот умножение почему-то нет. Ж(

А если бы умножение - ДА, то скольких проблем можно было бы избежать.

Dron |04.04.2006 21:25
Комментарии: 558


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

Можно конечно косвенно, в зависимости от контекста... но это ж не даст 100% гарантии.

Alexandoros |04.04.2006 21:53
Комментарии: 5

Зарегистрирован: 01.06.2005 10:01

Dron есть такая хрень - называется перегрузка операторов. В частности умножения.

Но все равно дебилоиды, как можно за раз запросить 4Г памяти??? Это что ж у них там объекты такой величины? Или они буфер для команд ссш решили побольше сделать, чтоб не парится???

Vadim Ushakov |05.04.2006 04:30
Комментарии: 78

Зарегистрирован: 18.08.2005 04:25

есть такая хрень - называется перегрузка операторов. В частности умножения.

В Си НЕТ перегрузки операторов!


Но все равно дебилоиды, как можно за раз запросить 4Г памяти??? Это что ж у них там объекты такой величины? Или они буфер для команд ссш решили побольше сделать, чтоб не парится???

Как раз то, о чем я недавно писал в комментариях к какой-то другой новости. Священная мантра "Этого никогда не случиться...":
"Наша программа не будет использоваться через 30 лет..."
"Этот буфер не может превышать 64 килобайта..."
"Длина имени файла всегда ограничена 256 символами..."
И так далее.
Видимо, авторы OpenSSH не относятся к числу поклоннкиков этой манты, и правильно делают. Никаких искуственных ограничений в программе быть не должно. Будут данные размером в 3 гигабайта - будем работать, если памяти хватит.


Вот интересно какой кретин выделяет за раз память для 64к обджектов размером 65к???????? Что ж там выделять такого надо - то???? А если выделяеш масив из 200к элементов, то тут уже просто логики хватает проверить не заздоровый ли он?

Что лучше, проверять перед каждым malloc'ом с помощью "просто логики", или один раз написать безопасную функцию выделения и использовать ее???

Dron |05.04.2006 09:53
Комментарии: 558


Ну даже если перегрузить оператор...

70000 * 70000 = 605032704
где ошибка?

Как ее предварительно или постфактом отловить?

На самом деле результат должен быть 4900000000.
нет таких средств.

fedukoff |05.04.2006 10:16
Комментарии: 178

Зарегистрирован: 24.03.2005 17:32

Не знаю точных терминов, но...
Нам надо умножить две числа A * B.
находим наименьшее Na, где 2^Na >= A
и так же находим наименьшее Nb, где 2^Nb >= B.
Получается, что Na и Nb это наименьшее кол-во битов, необходимых для хранения чисел A и B соответственно.
При умножении A * B, будет необходимо под результат зарезервировать Na+Nb битов. И если эта сумма больше, чем разрядность, то переполнение практически неизбежно.
(Бугага)

brasset |05.04.2006 10:41
Комментарии: 28

Зарегистрирован: 02.02.2005 13:17

Не все так просто. Логарифм очень ресурсоемкая операция. А тут на каждом простейшем умножении нужно вычислить как минимум 2(!) логарифма. Про софтовую реализацию говорить вообще нечего, код будет просто ползать. Аппаратно (в процессоре) - добавит в процессор не один десяток тысяч транзисторов, и опять же катастрофично снизит производительность. Back to i386

fedukoff |05.04.2006 10:57
Комментарии: 178

Зарегистрирован: 24.03.2005 17:32

Да ладно тебе, brasset! Не бери близко к сердцу! Ведь уже выяснили, что существует флажок переполнения. А одна операция ветвления после умножения больших проблем не создаст, а, даже, наоборот, позволит избежать множества других проблем.
Всего то, при компиляции, добавить одну лишнюю инструкция в сгенерированый код умножения.
А что нужно сделать, так это слегонца подправить ГЦЦ. Айда!

Alexandoros |05.04.2006 10:57
Комментарии: 5

Зарегистрирован: 01.06.2005 10:01

Dron - результат в __int64, проверить, назад в int.

Что лучше, проверять перед каждым malloc'ом с помощью "просто логики", или один раз написать безопасную функцию выделения и использовать ее???

В их случае безопасная ф-ция им не поможет, ибо они наперед не знаю сколько памяти они выделяют.
Я вообще говорю про код - что это if ((ptr = malloc(num * size)) == NULL) ?????
Тут и ежу понятно что нужно if ((ptr = malloc(num * sizeof(OurStruct))) == NULL). Приблизительно зная размер структуры, который не будет изменятся в одном сеансе проги, проверить нужно только num.

Vadim Ushakov |05.04.2006 11:21
Комментарии: 78

Зарегистрирован: 18.08.2005 04:25

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

Для тех, кто в танке или просто невнимательно читал указанную в новости статью:

We recommend using calloc(3) whenever allocating multiple objects of the same size. The above code would be written as:

size_t num, size;
char *ptr;
...
if ((ptr = calloc(num, size)) == NULL)
return (NULL);

calloc(3) has checks to make sure the multiplication does not overflow.


Где и что здесь кому "не поможет"? Всё кристально ясно.

В их случае безопасная ф-ция им не поможет, ибо они наперед не знаю сколько памяти они выделяют.
Я вообще говорю про код - что это if ((ptr = malloc(num * size)) == NULL) ?????
Тут и ежу понятно что нужно if ((ptr = malloc(num * sizeof(OurStruct))) == NULL).


Ясно как белый день, что в примере объявлен size_t size просто для того, того, чтобы продемострировать проблему. В реальной жизни будет sizeof(blabla).

Приблизительно зная размер структуры, который не будет изменятся в одном сеансе проги, проверить нужно только num.

Повторяю вопрос. Что проще и безопаснее: "приблизительно зная размеры структуры, проверить num", или просто выделять память через функцию, которая безопасно обрабатывает ошибку переполнения?
"Если что-то делается больше одного раза - напиши для этого процедуру!"



Комментарии доступны только авторизованным пользователям, авторизуйтесь или зарегистрируйтесь на сайте здесь

© OSRC.info, 2004-2010.
Авторские права на любые материалы, авторы которых явно указаны, принадлежат их авторам. По вопросам публикации таких материалов обращайтесь к авторам.
Авторские права на любые другие материалы принадлежат OSRC.info.
Сайт является помещением библиотеки. Копирование, сохранение на жестком диске или иной способ сохранения произведений осуществляются пользователями на свой риск.
При использовании материалов сайта ссылка на OSRC.info обязательна.