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

    +3

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    #include "gc.h"
    
    static bool PointerNotOwnedByParentStackFrame(struct StackFrame *frame,
                                                  struct StackFrame *parent,
                                                  void *ptr) {
      return !(((intptr_t)ptr > (intptr_t)frame) &&
               ((intptr_t)ptr < (intptr_t)parent));
    }
    
    /**
     * Adds destructor to garbage shadow stack.
     *
     * @param frame is passed automatically by wrapper macro
     * @param fn takes one argument
     * @param arg is passed to fn(arg)
     * @return arg
     */
    void __defer(struct StackFrame *frame, void *fn, void *arg) {
      struct StackFrame *frame2;
      if (!arg) return;
      frame2 = __builtin_frame_address(0);
      assert(frame2->next == frame);
      assert(PointerNotOwnedByParentStackFrame(frame2, frame, arg));
      if (append(&__garbage, /* note: append() not included */
                 (&(const struct Garbage){frame->next, (intptr_t)fn, (intptr_t)arg,
                                          frame->addr})) != -1) {
        frame->addr = (intptr_t)&__gc;
      } else {
        abort();
      }
    }

    deferы в Сищечке

    https://gist.github.com/jart/aed0fd7a7fa68385d19e76a63db687ff

    Запостил: 3.14159265, 04 Июня 2020

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

    • Стековый «сбощик мусора›
      #ifndef GC_H_
      #define GC_H_
      
      #define gc(THING) defer(free, (THING))
      
      /**
       * Calls FN(ARG) when function returns.
       */
      #define defer(FN, ARG)                            \
        ({                                              \
          __typeof(ARG) Arg = (ARG);                    \
          /* force -fno-omit-frame-pointer and */       \
          /* prevent weird opts like tail call */       \
          asm("" : "+g"(Arg));                          \
          __defer(__builtin_frame_address(0), FN, Arg); \
          asm("" : "+g"(Arg));                          \
          Arg;                                          \
        })
      
      struct StackFrame {
        struct StackFrame *next;
        intptr_t addr;
      };
      
      struct Garbages {
        size_t i, n;
        struct Garbage {
          struct StackFrame *frame;
          intptr_t fn;
          intptr_t arg;
          intptr_t ret;
        } * p;
      };
      
      extern struct Garbages __garbage;
      
      int64_t __gc(void);
      void __defer(struct StackFrame *, void *, void *);
      
      #endif /* GC_H_ */
      Ответить
    • &(const struct Garbage) { ... }

      Это с какой версии такой изврат можно делать? Пиздец, почему просто локалку не заполнить над вызовом?
      Ответить
      • И всё ради того чтобы анскильные питушки могли написать fclose не в конце файлика, а defer в срединке.
        Ответить
        • А как они кстати конец функции ловят чтобы мусор чистить?
          Ответить
          • Переписывают адрес разврата.
            __defer(__builtin_frame_address(0), FN, Arg);
            ...
            frame->addr = (intptr_t)&__gc;

            https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
            Ответить
            • Ох, сурово.
              Ответить
              • Ну вот примерно такое ебанатство придётся тащить в каждую реализацию Сишки, если примут предложение.

                А ещё питушение с longjmp.
                Ответить
            • Я, кстати, приводил реальный пример трюка с подменой адреса разврата:
              https://govnokod.ru/24763#comment429753

              В «gcc» его можно поймать с помощью __builtin_frame_address(0).
              Ответить
            • > переписывают адрес разврата

              Т.е. со всякими защитами от нарушения control flow этот код несовместим? Заебись фича. Хуже были только гццшные "лямбды", которые вообще исполняемый стек требовали.
              Ответить
              • Да простейший флаг omit-frame-pointer вынесет его нахуй.

                > Хуже были только гццшные "лямбды", которые вообще исполняемый стек требовали.
                Лолчто???
                Ответить
                • Они на стеке генерят переходник и возвращают его адрес.
                  Ответить
                  • Это же attack-friendly-programming.

                    Можно же просто насрать в стек своим юзер-инпут говном, а потом порушить всё нахуй.
                    Ответить
                    • Трамплины упоминались как минимум тут:
                      https://govnokod.ru/13183
                      https://govnokod.ru/13162
                      https://govnokod.ru/23505

                      Ещё где-то было.
                      Ответить
                    • Вспомнил реальный пример attack-friendly-programming:
                      https://govnokod.ru/19017

                      В 64-битном «MSVC» нет встроенного асма. Что предложил автор примера? Он предложил положить мышиного кота в строковую инициализированную переменную и снять атрибут «no execute» у страниц, в которые попало это значение.
                      Ответить
                      • Ну тогда уж в отдельную RX секцию можно отправить эту строку. Один фиг под конкретный конпелятор.
                        Ответить
                        • Можно. На платформах, на которых можно создать эту секцию. У «Portable executable» («Windows», например) и у «ELF» нет проблем. А на какой-нибудь «Макоси» можно обломаться.
                          Ответить
                        • Кстати, если мы пишем модуль для «EFI»/«UEFI», то можно пользоваться всеми фишками формата «PE» или есть ограничения?
                          Ответить
                          • Ну там как минимум импортов и экспортов нет. И атрибуты на секциях только недавно стали учитывать, раньше все как RWX грузили.
                            Ответить
                            • Какой багор )))
                              Ответить
                              • Да ладно, в виндовом ядре буквально лет 5 назад тоже был полный RWX.
                                Ответить
                                • Какой багор )))
                                  Ответить
                                  • Да ладно, в «DOS» буквально лет 30 назад тоже был полный RWX.
                                    Ответить
                                    • Как будто в «DOS» сейчас не полный RWX... Хотя в программах для DPMI (в том же «Doom» и в «Duke Nukem») можно установить защиту (когда их писали, не было процессоров с аппаратной поддержкой флага NX, но от записи страницу кода защитить было можно).
                                      Ответить
                                • а когда починивать начали? Когда все перешли на x64?
                                  Ответить
                                  • Аппаратное «DEP» на «Интелах» впервые появилось на x64.

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

                                    Удобной настройка исключений «DEP» в «Windows» стала, когда появилось средство «EMET»:
                                    https://en.wikipedia.org/wiki/Enhanced_Mitigation_Experience_Toolkit

                                    P.S. А, он про ядро, а не про юзерспейс.
                                    Ответить
                                  • То есть юзерспейс они защитили, а в ядре защиты долгое время не было в надежде, что драйвера гадить системе не будут?
                                    Ответить
                                    • Ну видимо да.
                                      Драйверы все таки подписанные, да они и так могут всё сломать
                                      Ответить
                                  • Начали где-то с восьмёрки, но всем было похуй.

                                    А реально прижали всех к стенке на десятке когда HVCI запилили, насколько я помню.
                                    Ответить
                                    • Что такое HVCI?

                                      P.S. Нагуглил:
                                      https://docs.microsoft.com/en-us/windows/security/threat-protection/device-guard/enable-virtualization-based-protection-of-code-integrity

                                      Его ещё в старых билдах не было... Как всё сложно...
                                      Ответить
                                      • Анальный гипервизор, который не разрешает ядру исполнять неподписанный код.
                                        Ответить
                                        • Он в минус первом кольце сидит что ли?
                                          Ответить
                                          • Эм, ну да, как и другие гипервизоры.
                                            Ответить
                                          • Помимо минус первого кольца, кстати, в дивном новом мире существует и минус второе третье, называется «Intel ME» («AMD PSP» для любителей красного).
                                            UPD: оказывается, минус второе — это «System Management Mode».
                                            Ответить
                                            • Ну ME это всё-таки не кольцо, а отдельный большой брат, который без основного проца работать может.
                                              Ответить
                                              • В общем-то и другие отрицательные кольца — не совсем «кольца» в «классическом» смысле. Тут скорее речь про привилегии и возможности воздействия.
                                                Ответить
                                                • а другие кольца про что?
                                                  Ответить
                                                  • Смотря в каком месте. Если, к примеру, ты попадёшь на ночную тусовку любителей x86, то про «CPL».
                                                    Ответить
                                                    • >тут скорее речь про привилегии
                                                      >CPL

                                                      Расшифруй мне пожалуйста выделенную жирным букву
                                                      Ответить
                                                      • Что тебе не нравится? В «x86» есть только четыре «стандартных» — определяемых CPL — кольца, а все отрицательные кольца — понятия не строго технические, а эмпирические.
                                                        Гипервизор, например, считается минус первым кольцом, «SMM» — минус вторым, «Intel ME» — минус третьим. Однако, повторюсь, это больше эмпирическое разделение по принципу «кольцо N имеет произвольный доступ к кольцам N+i, кольцо N не может влиять на кольца N-i больше дозволенного этими кольцами». Что именно считать отрицательным «кольцом» — вопрос философский. Физический мир, например, можно записать в минус четвёртое: компьютер может влиять на окружающую среду только через строго определённые механизмы, в то время как окружающая среда может воздействовать на компьютер как угодно.
                                                        Ответить
                                                        • Мне не нравится, что ты противопоставил Code Privilege Level привилегиям. И то и другое привелегии же.

                                                          Формально SMM, Hypervisor, и кстати v86, не являются "кольцами", хотя суть у них та же.


                                                          Привилегии виртуализировать ресурсы, привилегии обрабатывать чужие ошибки, привелегии прерывать чужой код
                                                          Ответить
                                                          • Я противопоставил кольца «технические» (0-3) кольцам «философским» — отрицательным.
                                                            «Технические» кольца (по крайней мере, в «x86») определены строго: через CPL. «Философские кольца», в свою очередь, чисто эмпирические: все просто договорились считать гипервизор минус первым кольцом, например. С тем же успехом его можно записать в минус десятое (SMM — -11, etc.), от этого смысл не изменится.
                                                            Ответить
                                                            • Наконец я тебя понял.
                                                              Ты предлагаешь не считать кольцом то, чей номер не может быть записан в CPL?
                                                              Ответить
                                                              • Нет, я предлагаю просто разделять кольца как техническое решение (CPL, например) и кольца как философское понятие (гипервизор, «SMM», окружающая среда).
                                                                Ответить
                                            • ME — это вообще отдельный процессор. ME первого поколения был на рахиттинктуре «ARC», а ME второго поколения — уже на x86 (80386 или типа того).

                                              SMM (минус второе кольцо) точно был на всех 80486, а также на каких-то 80386, только он, кажется, раньше не был документирован, кто-то случайно раскрыл опкод для переключения в SMM.
                                              Ответить
                                              • Да, SMM был нужен кажется изначально для APM (в эпоху до ACPI) чтобы усыплять комп при длительном бездействии (например при презентации, лол) или еще что-то такое же бессмысленное делать.

                                                Через SMM комп пыталась поломать Йоанна Рутковская
                                                Ответить
                                        • а, помню эту тему. теперь у нас сама винда в виртуалке
                                          Ответить
                • У Борманда неточность: не лямбды, а замыкания.

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

                  Зачем-то добавили фичу: на вложенную функцию можно взять указатель. Чтобы по этому указателю вложенную функцию можно было вызвать, придумали «трамплины»: это такой короткий код, который передаёт вызываемому замыканию указатель на контекст. Так вот авторы не придумали ничего лучше, чем создавать этот трамплин в стеке. Поэтому со всех страницы стека приходится снимать атрибут «no execute», иначе трамплин не вызовется.
                  Ответить
                  • Всё верно.
                    Лямблия просто онанимная функция. Она может зохватывать кого-то, а может и не зохватывать.
                    Замыкание же всегда зохватывает, но может быть и не онанимной.

                    "use strict";
                    
                    let someVar = 42;
                    
                    (() => {
                        console.log("Lambda, not closure");
                    }).apply();
                    
                    function closureNotLambda() {
                        console.log(`Closure, not lambda ${++someVar}`);
                    }
                    
                    closureNotLambda();



                    > Поэтому со всех страницы стека приходится снимать атрибут «no execute»

                    М-да, в base-system OpenBSD бы вас не приняли: там W^X
                    Ответить
                    • https://man.openbsd.org/gcc-local.1
                      https://man.openbsd.org/mprotect.2

                      Кажется, в юзверьском коде такого ограничения нет.
                      Ответить
                      • Есть. Для того, чтобы сказать _WRITE и _EXEC одновременно, нужно иметь спец ключик в заголовке (то-есть верно слинковаться).

                        А с этим ключиком тебе не дадут запуситься, если FS не прикручена со спец ключом. А ключ не стоит у раздела с base system.

                        Таким образом, в base system такого кода быть не может
                        Ответить
                        • Как всё сложно. Хорошо, что в «PHP» такого нет.
                          Ответить
                          • В PHP такого действитлеьно нет.

                            PHP ставится в другой раздел (/usr/local/), не является частью базовой системы, и потому может хоть все страницы пометить как RWX, всем насрать.

                            Тео и Ко гарантируют безопасность и безбажность только базовой системы. Охуилон портов всякого говна они, разумеется, не могут проверить.
                            Ответить
              • Можно играть: пишешь говно в стек, и стараешься сделать так, чтобы при джампе туда вываелась какая-нить хирня
                Ответить
                • Можно грабить корованы.
                  Ответить
                  • Здраствуйте. Я Noodles. Хочу чтобы вы сделали шеллкод, return-oriented-экшон. Суть токова... Можно напихать в стек адресов, содержащих нужные инструкции и данные, инструкции ret набигают и составленный в стеке шитый код исполняется. Можно заражать программы...

                    PS. я джва года жду такой шеллкод.
                    Ответить
                    • Да с исполняемым стеком можно и обычного кода напихать, без этого вашего ROP. Пачку nop'ов в начало и поехали.
                      Ответить
            • Ебать, Бедный ГЦЦ.
              Эти люди еще над SEH смеялись.
              Ответить
      • А чо не так, у структуры созданной из составного литерала нельзя адресс брать или шо.

        Вроде обычный C99.
        Ответить
        • Я так понимаю, что структуру инициализировали, и сразу взяли адрес. Это немного необычно.
          Ответить
          • я ВСЕГДА ТАК ДЕЛАЛ.
            Ответить
            • gcc работает, а vc нет
              struct Garbage
              {
              	int i;
              };
              
              
              void foo(struct Garbage* a)
              {
              	a->i = 12;
              }
              
              int main()
              {
              	foo(&(struct Garbage){.i = 12});
              	return 0;
              }


              Видимо потому, что vc не умеет c'99.

              А где будет создаваться структура, кстати? на стеке мейна, просто неявно?
              Ответить
              • vc ВООБЩЕ НЕ УМЕЕТ сИ, УБЕРИ НАХУЙ ЭТО ГОВНО ОТСЮДА.

                в ТВОЕМ ПРИМЕРИ МОЖЕТ БЫТЬ НА СТЕКЕ, ЛИБО В СЕКЦИИ С КОНСТАНЦИЯМИ, ЛЕНЬ ИСКАТЬ В СТОНДАРТЕ. я, СОБСНО, ИСПОЛЬЗОВАЛ ЭТУ ФИЧУ С ГЛОБАЛЬНЫМИ КОНСТАНТАМИ, КОГДА СВЯЗНЫЕ СПИСКИ СТРОИЛ, ЧТОБЫ НЕ ДЕЛАТЬ ЛИШНИХ ОБЪЯВЛЕНИЙ.
                Ответить
                • VC умеет C90.
                  Помню, что оно не умеет VLA, лол.
                  Да, если оно const, то наверное можно его в константы, а иначе наверное на стеке.

                  В общем неождианно, что такое есть в си, но правда удобно
                  Ответить
                • Что вы орёте-то?..
                  Ответить
    • > Си
      > gc.h

      Дальше не читал, но поорал.

      Это как с conio
      Ответить
    • Белый, белый, белый си, кобень за плечами.
      Белый, белый, белый си, снится мне ночами.
      Белый, белый, белый пи, анскильные птицы.
      Белый, белый, белый си, мне ночами снится.
      Ответить
    • Анскильный питух, жара, июнь
      Вореции такие сложные
      Только ты не пушишь никому
      Ждёшь ты только стека, стека, стека
      Ответить
    • https://github.com/jart - Justine Tunney
      https://en.wikipedia.org/wiki/Justine_Tunney
      Categories: ... Transgender and transsexual computer programmers
      Ответить
      • Если же говорить по поводу самого кода, то это ненужные костыли, к тому же завязанные на гнутые расширения.
        Ответить
      • так он был пацаном, когда чото там насрал в подростковой игрушке, а потом решил девочкой-леваком стать?
        Ответить
        • Я был когда-то странным
          Разрабом безымянным
          Которому реквестов
          Никто не заведёт
          Теперь я трансоняшка
          Мне каждая дворняжка
          При встрече сразу
          Лапу подает!
          Ответить
      • Если программистов стали делить по сексуальной ориентации, значит, сексуальная ориентация влияет на качество кода?
        Ответить
        • Программисты транссексуальных компьютеров.
          Ответить
          • Это про те самые ARM'ы у которых endian сменили?
            Ответить
            • На ARM можно менять не только endianess. У них ещё сетку инструкций менять можно: «thumb» и тому подобное.
              Ответить
              • Не у всех. На контроллерах, к примеру, только thumb2 обычно чтобы флешку не тратить.
                Ответить
                • Какой багор )))

                  А есть ещё процессоры с переключателем набора инструкций типа «ARM» или «NEC V20»?
                  Ответить
                  • x86_64
                    Ответить
                    • Можно даже без зелёного, кстати.
                      Ответить
                      • Действительно. Когда ещё был x86_32, там в разных режимах различались только размеры непосредственных констант (два байта или четыре), а в 64-битном режиме x86_64 куча инструкций поехала: удалили однобайтовые инкременты/декременты, чтобы освободить место под REX-префиксы.
                        Ответить
            • У «NEC» были девайсы «V20» и «V30», которые в обычном режиме исполняли инструкции 8086/8088, но был ещё режим исполнения инструкций 8080, причём во время прерывания режим корректно переключался.
              Ответить

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