1. Си / Говнокод #19466

    −106

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    int stupid(int a)
    {
        return a + 1 > a;
    }
    
    int main(void)
    {
        int a = -1;
        printf("%d\n", stupid(a));
        return 0;
    }

    И вот так один UB портит всё.

    ~ $ gcc -c test.c -O3
    ~ $ ./test
    1


    Ибо:
    Disassembly of section .text:

    0000000000000000 <stupid>:
    0: b8 01 00 00 00 mov $0x1,%eax
    5: c3 retq

    Запостил: codemonkey, 15 Февраля 2016

    Комментарии (53) RSS

    • Ну если не пихать туда INT_MAX, то все нормально
      Ответить
    • Что люди только не делают лишь бы limits.h не подключать
      Ответить
    • Ну общее правило же - компилятор имеет право предполагать, что программист избежал UB.
      Ответить
    • (-1+1) > -1 = 1; какие проблемы то?
      Ответить
      • (0x7FFFFFFF + 1)
        Ответить
        • Программист не будет складывать знаковый INT_MAX с единицей!
          Ответить
          • …, подумал компилятор.
            Ответить
            • Один бывший коллега на полном серьёзе считал что 0x7fffffff + 1 == 0x80000000 и что это well-defined.

              Приходилось тыкать носом в Стандарт.
              Ответить
              • Для беззнаковых - да.
                Ответить
              • по стандарту то да, но вот вопрос: а есть компиляторы, в которых операция INT_MAX+1 реализована иначе как = INT_MIN?
                Ответить
                • Well, гцц и шланг тупо меняют (INT_MAX + 1) на INT_MIN.
                  16:   be 00 00 00 80          mov    $0x80000000,%esi


                  Я бы сгенерил system("rm -rf / --no-preserve-root").
                  Ответить
                  • > Я бы сгенерил system("rm -rf / --no-preserve-root").
                    Но зачем? Более, чем в половине программ встречается такая архитектура, где целые числа не крутятся в цикле, и преобразование из INT_MAX в INT_MIN выразится в форме
                    if(x == INT_MAX) {
                      x = sin(j, xi*pi+eps(8.0)) * exp(A(9,9,4) ^^^^ 16) + 0.1;
                    } else {
                      x = x + 1; 
                    }

                    и это отдельное преоразование будет стоить стандарту миллиарды грустных пользователей, у которых x:=x+1 выполнилось за 3нс вместо 2нс?
                    Ответить
                    • Вот иногда хочется спросить — неужели сложно было сделать, чтобы процессор сам вызывал прерывание при переполнении инта? Стека? Выходе за страницу? Попытке исполнения кода в области, помеченной как данные?
                      Ответить
                      • > неужели сложно было сделать, чтобы процессор сам вызывал прерывание при переполнении инта?
                        На х86 переполнение — штатная ситуация при сложении отрицательных чисел — дополнительный код так работает. А интмакс+1 так вообще ничего необычного. Флажок ставится, впрочем. Можно спросить, переполнила ли предыдущая операция регистр.

                        > Стека? Выходе за страницу?
                        Так он это делает. А потом видишь «программа выполнила недопустимую операцию и будет закрыта»

                        Попытке исполнения кода в области, помеченной как данные?
                        Есть, гугли Data Execution Prevention и NX bit
                        Ответить
                        • Почему-то я раньше думал, что это всё реализовано на программном уровне, иначе почему огромное количество разных уязвимостей нулевого дня связано именно с переполнением?
                          Ответить
                          • Ну потому что переполнение буфера на стеке даже при неисполняемом стеке можно заюзать ;)

                            Гугли return oriented programming.

                            Полностью добить все эти уязвимости смогли только через NX + ASLR (рандомизация адресов загрузки) и то, походу, только в линухе. Потому что M$ так и не смогло решиться и послать в пизду всех рукожопых разрабов со всем их ASLR и NX несовместимым говном...
                            Ответить
                            • Таки меня давно интересовал этот вопрос: NX + ASLR на винде реально работают или это так, туфта? Желательно с пруфами.
                              Ответить
                              • Если экзешник и ВСЕ используемые им dll'ки помечены как ASLR/NX совместимые - должно работать :) Как там с этим на практике - хер знает, не интересовался.
                                Ответить
                          • Вспоминаются драйвера на какой-то Canon'овский принтер, из-за которых приходилось отключать DEP в XP, иначе проги крашились при печати...
                            Ответить
                            • Причём, емнип, крашилась какая-то dll'ка и отключать DEP надо было именно в той проге, из которой печатаешь (т.е. браузеры, офис и т.п.) А они то как раз самые уязвимые из-за своей сложности и работы с недоверенным, хуй пойми откуда взятым, контентом...
                              Ответить
                              • Борманд спрашивает Борманда - "какова твоя профессия, Борманд?"...
                                Ответить
                                • До этого лета - "специалист широкого профиля" из маленького городка, коих презирают программисты из германии, выдрочившие до блеска свой язык и технологию.

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

                                  А теперь просто c++-няшка с уклоном в системщину, работающая в одной из международных контор.
                                  Ответить
                                  • >"специалист широкого профиля", коих презирают программисты из германии, выдрочившие до блеска свой язык и технологию.
                                    По моему имхо, с таким набором из тебя бы вышел разве что юниор - хоть у вас, хоть где. Проггер и админ (по твоему описанию - так вообще эникей) все-таки разные вещи. Кто сказал что их презирают? Они полезные ребята, все професси нужны, уборщицы вон тоже, без них столько пыли заводится :)
                                    Ответить
                                    • > разве что юниор
                                      Ну если не угробить почти всё своё свободное время (включая рабочее, когда болтаешь по телефону с клиентом и т.п.) на самообразование - таки да, возможно и получился бы юниор.
                                      Ответить
                                      • А так кто получился?
                                        Ответить
                                        • > А так кто получился?
                                          Бородатый анимешник.
                                          Ответить
                                          • В профессиональном плане. Анимешник это не профессия, это форма проеба свободного времени.
                                            Ответить
                                            • Ну Борманд может стать критиком анимэ и зарабатывать на этом деньги. Тогда это будет профессия
                                              Ответить
                                              • > может стать критиком анимэ
                                                Да ну... Это у меня просто такая форма эскапизма и экранировки от всех проблем, что в мире происходят.
                                                Ответить
                                                • Всяко есть что то такое, чем тебе просто нравится заниматься не смотря не на что.
                                                  Ответить
                                              • Гы, ну я чего-то вторым БОксером не стал.
                                                Ответить
                                            • > В профессиональном плане.
                                              Ну как выше и написано - c++ developer с уклоном в системщину.

                                              Без приставки senior пока что, если быть честным. Ну и пофиг, я не кармадрочер. А задачки один фиг не джуновские.
                                              Ответить
                                              • А какие вообще бывают приставки и что они означают?
                                                Ответить
                                  • Он говорил и говорил, а Кегдан достал очередную сигарету, и, закурив, представил как космические корабли бороздят бескрайние просторы космоса. Временами ему казалось, что комнату заполняет мягкий гул ионных двигателей, и он улыбался.
                                    - А теперь просто c++ developer с уклоном в системщинку
                                    - Что?
                                    - c++ developer с уклоном в системщинку
                                    - Ааа. Удачи тебе с этим - пробормотал Кегдан и выкинул сигарету в окно
                                    Ответить
                          • В первую очередь потому, что огромное количество софта с этой защитой несовместимо.

                            Кстати, какое сообщение выдается при срабатывании NX? Програма выполнила невыполнимое, допустила недопустимое или память не может быть "read"/"written"?
                            Ответить
                            • Как-то так: https://i-technet.sec.s-msft.com/dynimg/IC11686.gif
                              Ответить
                              • Э-э-э... У меня такого никогда не было, а вот старые проги от включенного депа падали. Ты не ошибся?
                                Ответить
                • В отрыве от других операций - вроде нет. Но вот, к примеру, если старший бит или знак посмотреть, то гцц оптимизнёт и всегда будет возвращать ноль/плюс.
                  Ответить
                  • надо в линухе поковырятся. я только читал что были флеймы по поводу этой оптимизации в гцц, которая основательно сломала кернел. там где-то же должны быть и воркараунды.

                    но за исключением кернела/подобного, я даже и не знаю где еще народ переполнения корректно обрабатывает. с приходом буилтинов шланговых ( гцц тоже умеет - https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html ) как по мне все стало проще.
                    Ответить
                  • то есть оптимизированный gcc (INT_MAX+1)<0 вернет false?
                    Ответить
                    • Если (INT_MAX+1)<0 будет прямо написано в коде, то он скорее всего вернёт true. Но если с нулём будет сравниваться сумма гарантировано положительных чисел, то проверка будет выкинута и заменена на true.

                      http://goo.gl/kNnjKh
                      Ответить
                      • *заменена на false: http://goo.gl/6Qvnz1
                        Ответить
                        • Вот что случается если компилятору дать понять, что есть ровно 2 случая — сравнивать с нулём 0 + 1 или INT_MAX + 1: http://goo.gl/VKSs9U

                          Учитывая, как компилятор преобразовывает INT_MAX + 1, я ожидал, что он оптимизирует это до return a == 0, но нет, компилятор продолжает показывать рис. 1 всем, кто считает что он что-то должен в случае UB

                          #TalkingWithMyself
                          Ответить
        • не надо путать int(-1) = int(UINT_MAX) = 0xFFFFFFFF с int(INT_MAX) = 0x7FFFFFFF
          Ответить
    • По такой логике int вообще нельзя использовать, вдруг он INT_MAX будет равен.
      Ответить
      • Это спарта. В си даже два числа не так-то просто сложить, не триггернув потенциальный UB (по крайней мере, без асма и интринсиков).
        Ответить
        • А в Борманд Паскале с директивой $R+ целочисленные выражения проверяются на потенциальный выход за пределы диапазона...

          Да и gcc можно пересобрать с опциональным bounds checking.
          Ответить
          • > с директивой
            > опциональным
            А потом кто-то соберёт твою прогу/либу без этих опций или другим компилятором... Решение не сильно лучше интринсиков или асма...
            Ответить
          • int safe_add(int a, int b, int* res)
            {
                if (a < 0) {
                    if (b < 0) {
                        int limit = INT_MIN - b;
                        if (a < limit)
                            return 1;
                    }
                } else {
                    if (b >= 0) {
                        int limit = INT_MAX - b;
                        if (a > limit)
                            return 1;
                    }
                }
                *res = a + b;
                return 0;
            }
            http://ideone.com/P6JB5w
            Ответить
      • Всё верно. Сравнение для плавающего питуха gcc не выкидывает даже с -O3, значит, целые лучше не использовать.
        Ответить

    Добавить комментарий