Undeadly: Умножение - небезопасная операция В своем блоге разработчик OpenBSD рассказывает () об опасностях выхода за разрядную сетку при умножении в C, схожая ошибка стала причиной уязвимости OpenSSH в 2002 году, первой и единственной на данный момент удаленной уязвимости OpenBSD в стандартной установке за все время ее существования. |
Комментарии |
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| Радует, что среди программистов всетаки встречаются люди, умеющие программировать. |
|
Комментарии: 10
Зарегистрирован: 03.07.2004 22:20
| Это ошибки и транслятора и архитектуры. Что еще раз говорит о недостатках современной архитектуры компьютера. |
|
Комментарии: 558
| Да, плохо то, что программисту приходится думать о том, не вылезет ли его умножение за допустимые рамки. об этом должен думать, ну если не процессор, то сама программа... и грязно ругаться... и кидаться эксепшинами... и падать в коры. Кстати вот странно. деление на ноль есть, а умножения нет... Ж)
Но поскольку, что имеем - то и имеем... Тот программист, который думает об этом - молодец. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| хмь. Я, конечно, не специалист (R), но, мне кажется, что после умножения, процессор (по крайней мере х86) устанавливает (или сбрасывает) флажок переполнения. Полуцаецо, что всеголишь одна команда ветвления после умножения и дело в шляпе. ЗЫ моя не есть утверждать, что наличее отутствия проблемы имеет место быть, но моя есть утверждать, что присутствие наличия данной проблемы довольно легко прекратить, тють-тють слегка модернизировав компиляторное программное обеспечение. |
|
Комментарии: 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.
Правда не понятно, что происходит в случае нарушения. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Не попробуешь, не узнаешь! |
|
Комментарии: 558
| О, точно, попробовать! Ж))
Не, не то... унрекогнайзед опшин на i686-pc-linux-gnu
видимо это для x86-64, типа можно eax умножать, а можно rax... но я не могу этого проверить.
В любом случае проверкой не пахнет. |
|
Комментарии: 5
Зарегистрирован: 01.06.2005 10:01
| size_t у гсс == int? Вот интересно какой кретин выделяет за раз память для 64к обджектов размером 65к???????? Что ж там выделять такого надо - то???? А если выделяеш масив из 200к элементов, то тут уже просто логики хватает проверить не заздоровый ли он? |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Я есть согласный! Если операция имеет возможность поломать чтонидь, то надо результат проверить. И результат на лицо |
|
Комментарии: 558
| Здравый смысл - здравым смыслом, но всетаки нелогично... ошибка деления - есть... ошибки умножения - нету Ж(
даже границы массивов контролируются компиляторами... а вот умножение почему-то нет. Ж(
А если бы умножение - ДА, то скольких проблем можно было бы избежать. |
|
Комментарии: 558
| Вы вот говорите взять, да проверить.. Но беда в том, что программист на си не имеет физической возможности проверить результат умножения.
Можно конечно косвенно, в зависимости от контекста... но это ж не даст 100% гарантии. |
|
Комментарии: 5
Зарегистрирован: 01.06.2005 10:01
| Dron есть такая хрень - называется перегрузка операторов. В частности умножения.
Но все равно дебилоиды, как можно за раз запросить 4Г памяти??? Это что ж у них там объекты такой величины? Или они буфер для команд ссш решили побольше сделать, чтоб не парится??? |
|
Комментарии: 78
Зарегистрирован: 18.08.2005 04:25
| есть такая хрень - называется перегрузка операторов. В частности умножения.
В Си НЕТ перегрузки операторов!
Но все равно дебилоиды, как можно за раз запросить 4Г памяти??? Это что ж у них там объекты такой величины? Или они буфер для команд ссш решили побольше сделать, чтоб не парится???
Как раз то, о чем я недавно писал в комментариях к какой-то другой новости. Священная мантра "Этого никогда не случиться...": "Наша программа не будет использоваться через 30 лет..." "Этот буфер не может превышать 64 килобайта..." "Длина имени файла всегда ограничена 256 символами..." И так далее. Видимо, авторы OpenSSH не относятся к числу поклоннкиков этой манты, и правильно делают. Никаких искуственных ограничений в программе быть не должно. Будут данные размером в 3 гигабайта - будем работать, если памяти хватит.
Вот интересно какой кретин выделяет за раз память для 64к обджектов размером 65к???????? Что ж там выделять такого надо - то???? А если выделяеш масив из 200к элементов, то тут уже просто логики хватает проверить не заздоровый ли он?
Что лучше, проверять перед каждым malloc'ом с помощью "просто логики", или один раз написать безопасную функцию выделения и использовать ее??? |
|
Комментарии: 558
| Ну даже если перегрузить оператор...
70000 * 70000 = 605032704 где ошибка?
Как ее предварительно или постфактом отловить?
На самом деле результат должен быть 4900000000. нет таких средств. |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Не знаю точных терминов, но... Нам надо умножить две числа A * B. находим наименьшее Na, где 2^Na >= A и так же находим наименьшее Nb, где 2^Nb >= B. Получается, что Na и Nb это наименьшее кол-во битов, необходимых для хранения чисел A и B соответственно. При умножении A * B, будет необходимо под результат зарезервировать Na+Nb битов. И если эта сумма больше, чем разрядность, то переполнение практически неизбежно. (Бугага) |
|
Комментарии: 28
Зарегистрирован: 02.02.2005 13:17
| Не все так просто. Логарифм очень ресурсоемкая операция. А тут на каждом простейшем умножении нужно вычислить как минимум 2(!) логарифма. Про софтовую реализацию говорить вообще нечего, код будет просто ползать. Аппаратно (в процессоре) - добавит в процессор не один десяток тысяч транзисторов, и опять же катастрофично снизит производительность. Back to i386 |
|
Комментарии: 178
Зарегистрирован: 24.03.2005 17:32
| Да ладно тебе, brasset! Не бери близко к сердцу! Ведь уже выяснили, что существует флажок переполнения. А одна операция ветвления после умножения больших проблем не создаст, а, даже, наоборот, позволит избежать множества других проблем. Всего то, при компиляции, добавить одну лишнюю инструкция в сгенерированый код умножения. А что нужно сделать, так это слегонца подправить ГЦЦ. Айда! |
|
Комментарии: 5
Зарегистрирован: 01.06.2005 10:01
| Dron - результат в __int64, проверить, назад в int.
Что лучше, проверять перед каждым malloc'ом с помощью "просто логики", или один раз написать безопасную функцию выделения и использовать ее???
В их случае безопасная ф-ция им не поможет, ибо они наперед не знаю сколько памяти они выделяют. Я вообще говорю про код - что это if ((ptr = malloc(num * size)) == NULL) ????? Тут и ежу понятно что нужно if ((ptr = malloc(num * sizeof(OurStruct))) == NULL). Приблизительно зная размер структуры, который не будет изменятся в одном сеансе проги, проверить нужно только num.
|
|
Комментарии: 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", или просто выделять память через функцию, которая безопасно обрабатывает ошибку переполнения? "Если что-то делается больше одного раза - напиши для этого процедуру!" |
Комментарии доступны только авторизованным пользователям, авторизуйтесь или зарегистрируйтесь на сайте здесь
|