LINUX.ORG.RU

Си. Почему бы не запретить запись в стек?

 


1

4

Решил немного разобраться как работают уязвимости. Как я понял, весомая их часть модифицирует стек.

#include <stdio.h>

register long unsigned rsp asm("rsp");

void print_arg(int arg) {
    ((int*)rsp)[3] = 0xBADC0DE;
    printf("arg = %x\n", arg);
}

int main(int argc, char **argv) {
    print_arg(0xF00D);
    return 0;
}

Этот код отрабатывает и не выводит ошибкок с

-fhardened -fcf-protection=full

На мой взгляд выглядит небезопасно.

Почему бы не вставлять проверки на ассемблере при записи в память, на включаемость в регион стека? Если нужно записать что то в аргумент на стеке (int), то проверку можно не вставлять. При записи по указателю, уже обязательно вставлять. Если адрес стека то ошибка. В memset проверять пересечение двух диапазонов.

Если на стек пишет fread(), то нужно вставить еще проверку на разрешающий диапазон буфера в который он пишет. Но тут компилятору нужно обязательно отслеживать откуда приходит поинтер, но если мы говорим про значения на стеке, то это не должно представлять сложности, значение всегда лежит в прошлых значениях стека, а значит вычислить размер буфера можно в момент компиляции, ну или записать в какую нибудь дополнительную переменную на стеке, если речь о сложном потоке управления и alloca.

void read_file(const char *name)
{
        char buff[999];
        FILE *f = fopen(name, "rb");
        read_block(f, buff);
}

void read_block(FILE *f, char *buff)
{
        // тут компилятор должен вывести что len(buff) == 999
        fread(buff, 1, 9999, f);
}

Что бы все идеально работало, нужно будет:

  • Пометить libc функции
  • Если функция работает со стеком как у меня в верхнем примере, но это правильное поведение, пометить и ее
  • Перекомпилировать основные библиотеки, что бы не ломать ABI можно ввести экспорт двух прототипов, с доп.значениями для проверки диапазонов и без, дублирование прототипов понадобится для малого числа функций
★★★★★

Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от watchcat382

Функция malloc должна запрашивать память у ядра, которое озаботится созданием сегмента нужного размера.

Правильно ли я понял что будет по syscall-у при создании/удалении каждой переменной?

V1KT0P ★★
()
Ответ на: комментарий от alysnix

у вас функция с локальными переменными вызывает сама себя 10 000 раз…

Это называется эффективный код? Просто не надо так писать тогда не придется преодолевать собственноручно созданные трудности. И вообще интересно - для какой задачи именно такой странный способ решения может потребоваться? Да так,что его невозможно/неэффективно заменить другим способом,не требущим вложенных вызовов такой глубины.

watchcat382
()
Ответ на: комментарий от alysnix

если переменная накрыта личным сегментом, любой доступ к ней требует >загрузки сегментного регистра.

Это было медленно на 80286 потому что там таблица сегментов не кэшировалась и каждый раз за ней надо был в ОЗУ лезть. Уже в 386 был кэш,а в более новых - еще и несколько уровней кэша.

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

16 тысяч сегментов на одну программу - мало? Мне так не кажется. Тем более что переменные,к которым не производится доступ по индексу,способному вылезти за допустимый диапазон, помещать каждую в свой сегмент не обязательно. Кстати у линуксового линкера есть настройки,позволяющие либо объединять несколько секций из объектых файлов в одну секцию в исполняемом файле (по определенным условиям) либо этого не делать. Если мы будет отображать секции elf на аппаратные сегменты то это позволит управлять их потребным количеством.

работа с сегментами(создание/уничтожения) - небыстрое дело.

Опять же это утверждение тянется из документации на 80286 где небыло кэша. А также замечу,что повышение надежности исполнения абсолютно всегда приводит к некоторому снижению скорости. Но сейчас,в отличие от начала 90х, производительность процессоров чаще всего существенно превышает необходимую для решаемых задач. Так что небольшим снижением можно пожертвовать. Ну и если кому-то нужна именно пиковая производительность - они всегда смогут запихать код,стек и данные в один сегмент. Просто в их программе не будет защиты памяти. Но только в их,а в других будет.

watchcat382
()
Ответ на: комментарий от watchcat382

https://russianblogs.com/article/58804214432/ Управление памятью Linux: сегментированный механизм и механизм обработки памяти адресации памяти

Имеется глава и о защите памяти.

В организациях много старых компьютеров.
Скорее всего добавление в Linux возможности аппаратной защиты памяти будет полезно.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 2)
Ответ на: комментарий от V1KT0P

Правильно ли я понял что будет по syscall-у при создании/удалении каждой >переменной?

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

Программы разные бывают - если это какая-нибудь игрушка то ей нужна скорость исполнения,а надежность не критична. А если это к примеру какие-нибудь финансовые вычисления - то там ошибки могут привести к убыткам в реальных деньгах. И лучше бы считать не спеша но гарантированно без ошибок.

watchcat382
()
Ответ на: комментарий от watchcat382

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

если вас так парят индексы, значительно проще динамически проверять индекс, и быстрее, чем ворочать сегментами. что и делают все языки с защитой… типа явы или шарпа.

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

вы боретесь с ветряными мельницами. вопрос давно решен, от тегированной памяти (см эльбрус), до защищенных языков - смотри java, сишарп, голанг и всю эту кучу.

alysnix ★★★
()
Ответ на: комментарий от watchcat382

< Сейчас вон в линуксе вообще всё в один большой сегмент кладётся.

Вот что об этом пишут.

В беспомощности дизайнер Linux просто позволил адресу параграфа 0, а граница абзаца составляет 4 ГБ.
Кроме того, из -за требований механизма сегмента «объем смещения»

Forum0888
()
Ответ на: комментарий от alysnix

API, использующее метаданные имеется данные о полях и объектах, так что подумаю о целесообразности добавления контроля.
Скорее всего лучше для этого разработать подсистему для работы с памятью.

Вот и пользу от треда получил!

Forum0888
()
Ответ на: комментарий от Forum0888

В организациях много старых компьютеров.

И их будет становиться только больше потому что бизнес считает деньги и без объективных причин менять технику не склонен. Уже сейчас для большей части задач производительность персональных компьютеров сильно избыточна. Даже несмотря на искусственное их замедление рисованием всяких красивостей на экране,на собственно выполнение полезной работы влияющих либо никак либо отрицательно.

Скорее всего добавление в Linux возможности аппаратной защиты памяти будет полезно.

Благодарю за поддержку. Я за прошедшие четверть века (в Линуксе с 97 года) далеко не раз участвовал в дискуссиях на эту тему. И в большинстве случаев сталкивался с мнением типа «это никому не нужно». Причем чаще всего оно высказывалось людьми, весьма слабо понимающими о чем вообще разговор.

А самое главное, мне кажется что аппаратную защиту можно добавить даже не ломая совместимость. Просто софт,скомпилированный без поддержки аппаратной защиты - будет выполняться как есть,в едином сегменте. А те кому это надо - смогут пользоваться защитой если соберут свою программу так чтобы она была с ней совместима. То есть не занималась всякими трюками,нарушающими аппаратные правила. Причем вполне может быть,что некоторое число существующих программ получится научить работать с защитой просто перекомпилировав их. Если в них нет ничего «противозаконного» то заработают. Такая же ситуация была тридцать лет назад с дос-экстендерами - корректно написанные программы удавалось собрать в исполняемый формат NE из тех же объектников и они работали в защищенном режиме под экстендером.

watchcat382
()
Последнее исправление: watchcat382 (всего исправлений: 1)
Ответ на: комментарий от Forum0888

вопию в пустыне.

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

а для кучерявых абстракций есть эти самые защищенные языки. в них вы мимо массива не напишете(будет рантайи проверка индекса). и адрес не измените никакой арифметикой. и один тип в другой вам не дадут привести запросто так.

и все. а больше ничто и не нужно.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Не точно, но похоже всё же разработаю API для управления памятью.
Профит в том, что API, использующее метаданные имеет в run-time данные об используемых полях объектов, ...
То бишь вполне реально программно производить контроль доступа.
Что-бы сильно не тормозило можно сделать управляемый контроль.
Для этого будет достаточно лишь в свойствах полей установить некий флаг.
И усё!

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

Пока API кроссплатформенно и его можно использовать в любой ОС и железе.

«Нельзя объять необъятное», реализую, то что для разработки будет полезно.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 1)
Ответ на: комментарий от alysnix

если вас так парят индексы, значительно проще динамически проверять >индекс, и быстрее, чем ворочать сегментами.

Вот абсолютно не факт что быстрее! Медленность работы с сегментами - это фактически цитата из документации по 80286 где небыло кэширования. На 386 оно уже было. Но конечно имеет смысл написать тестовый код,который меряет скорость «ворочания сегментами» на процессорах,выпущенных в последние десяток лет. Как именно мерять - вопрос подлежит дальнейшему обсуждению.

что и делают все языки с защитой… типа явы или шарпа.

И оба - известные тормоза и пожиратели ресурсов. Именно потому что не используют заложенные в процессор аппаратные механизмы защиты, а эмулируют их программно. Ада(в реализации компилятора gnat) тоже эмулирует,но всё-таки побыстрее значительно. А мне приходилось видеть компилятор Meridian ADA,который пользоваться аппаратной защитой таки умел.

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

Так я и предлагаю дать программисту возможность воспользоваться именно этим достоинством. Вот нужна ему надежность исполнения и он хочет интерпретировать память так чтобы она была совместима с сегментным механизмом защиты. Согласно идеологии Си - имеет право! А если не нужно - сложит всё в один сегмент как сейчас. Наличие выбора лучше чем его отсутствие.

вы боретесь с ветряными мельницами. вопрос давно решен

Хорошо,зайдем с другой стороны если вам так непонятно. Я заплатил свои деньги за наличие в процессоре механизма защиты памяти. Почему я не могу хотеть им воспользоваться так как это было задумано ведущими инженерами фирмы Интел,а не,извините, финским студентом? Студент скорее всего просто схалявил как это свойственно большинству студентов(сам таким был:) и не стал разбираться как в своем дипломном(или каком там) проекте задействовать этот довольно сложный аппаратный механизм. Да и откуда он знал что его проект вдруг приобретет всемирную популярность.

watchcat382
()
Ответ на: комментарий от alysnix

а больше ничто и не нужно.

Вы бы для порядка что ли добавили к этому утверждению «мне кажется» или «я думаю». Потому что другие люди (вот я например) могут думать иначе. И аргументы для этого приводят вполне осмысленные (на мой взгляд).

И обозвать меня безграмотной школотой не получится потому что школу ( и высшую тоже:) я еще в прошлом веке закончил. :-)

будет рантайи проверка индекса

Только она будет программная. А я предлагаю использовать аппаратную,благо она в процессоре есть. Вот например на ARM,где такого аппаратного механизма нет, придется ограничиться программным.

В качестве аналогии - что-то многие пытаются задействовать встроенный видеоускоритель интеловских процов даже там где не занимаются никакой работой с графикой,а например код пишут и компилируют. Вот просто он есть значит надо использовать. И бухтят если не поддерживается. Аналогично, механизм сегментной защиты памяти в проце есть - имею право хотеть чтобы он работал на пользу меня любимого:)

watchcat382
()
Ответ на: комментарий от Forum0888

Не точно, но похоже всё же разработаю API для управления памятью.

Думаете, в одно лицо получится такое осилить? Вон, даже Торвальдс не осилил с всеми возможностями процессора разобраться,выбрал самую упрощенную модель памяти. Что довольно странно,ибо в мемуарах он где-то писал что копил деньги именно на 386 проц (что тогда было дорого) чуть ли не экономя на завтраках. В итоге комп он купил,а вот правильно использовать не смог.

watchcat382
()
Ответ на: комментарий от alysnix

для си это не работает, поскольку возможность интерпретировать память как хочешь

Нет конечно

#include <stdio.h>

void f(int* a, short* b) {
  *a = 0;
  *b = 1;
  printf("%d,%d\n", (int)*a, (int)*b); // Выводит 0, 1
}

int main() {
    int i = -1;
    f(&i, (short*)&i);
}

MOPKOBKA ★★★★★
() автор топика
Ответ на: комментарий от watchcat382

Не для Linux в целом, а для своего API.
А его уже 130MB в архиве!

У меня всё кроссплатформенно и работает на любой ОС (потому, что «гвозди» не использую).

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 2)
Ответ на: комментарий от Forum0888

Ради интереса поищите - «Linux FPGA».

Так а в чем тут интерес? Да, я читал что в каких-то монстрообразных и дико дорогих (тысячи баксов за шутку) FPGA можно запрограммировать какой-то процессор (вроде бы ARM) на котором можно потом запустить линукс. Но это настолько узкоспециальное и далёкое от повседневной жизни применение что я бы не стал его всерьез обсуждать. А вот в x86 процах защита памяти уже есть и эти процы у каждого из нас есть на столе. Просто надо полностью использовать предоставляемые ими возможности.

watchcat382
()
Ответ на: комментарий от Forum0888

А его уже 130MB в архиве!

130 мегов скомпилированного или исходников? В возможность в одно лицо даже просто наколотить на клаве 130 мегов текста я что-то не верю. Разве что чем-нибудь нагенерировать похожие повторяющиеся фрагменты. И всё равно даже для этого слишком много.

watchcat382
()
Ответ на: комментарий от alysnix

Не все велосипеды мои.
Делал форки для freetype, SDL, harbour, ... но от них лишь одни названия остались.
Постепенно перевожу их функционал на своё core.

Ведь «нельзя объять необъятное», а вот обобщить можно!

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 2)
Ответ на: комментарий от Forum0888

здесь речь о том, можно ли FPGA задействовать как сопроцессор?

Его придется подключать не в слот расширения,а между процом и памятью. То есть это тянет разработку собственной системной платы. Задача сильно выше радиолюбительского уровня если говорить о чем-то новее 80386(да и там очень не просто). Ну и стоимость таких монстрообразных FPGA - далеко не «домашняя». А главное - зачем,если в проце и так всё уже есть?

watchcat382
()
Ответ на: комментарий от Forum0888

API, использующее метаданные имеет в run-time данные об используемых полях объектов

емнип в жабе в недрах кровавого энтерпрайза была рантайм генерация бинов рефлексией из XML-ных портянок. Вообще рекомендую действительно посмотреть на машины жабы/дотнета, чего и как они там у себя делают с памятью и метаданнымт объектов.

arkhnchul ★★★
()
Ответ на: комментарий от arkhnchul

Если возникнет необходимость, то покопаю.
Java ранее годами использовал.
Ныне нет.

Java, Net - монстры, которые имеют сотни подсистем с тысячами методами, ...
То core, которое разрабатываю пригодно для разработки объектных ЯП, но не с использованием грамматик.
Ныне все «упёрлись» в грамматики и не хотят думать, да ещё фанатично верят, что грамматики - «панацея».

Всё как обычно.

Начал было разработку а-ля Си для 11-го стандарта.
Многое сделал, но вовремя приостановил разработку.
После разработки API для использования знаний пожалуй начну разработку ЯП, но не а-ля СИ, С++, Java, Net, ... .

В том же harbour VM весьма эффективна.
Не панацея конечно, но хороший пример того, как можно разработать эффективную VM.

Core harbour в какой-то мере универсальное и его можно использовать в любом проекте.

Использую из него лишь некоторое API, чтобы велосипед не
изобретать, но похоже через какое-то время от него полностью
откажусь, так разрабатываемое core много функциональней,

API, использующее метаданные эффективно и заточено для использования в run-time.
Подсистему работы с памятью разработаю.
Архитектура у неё будет заточена для разработки эффективного API.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 4)
Ответ на: комментарий от Forum0888

Почитайте https://ru.wikipedia.org/wiki/Кольца_защиты.

Это не о том. То что хочет автор топика делается через аппаратные сегменты как это было в Multics или 16 битных версия Windows.

X512 ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Указатели же равны?

Си теоретически позволяет указатели с разными доменами адресации. Например отдельная адресация кода и данных или аппаратные сегменты где адрес считается с начала сегмента. В таком случае равенство указателей само по себе ничего не значит, надо чтобы ещё домены/сегменты указателей совпадали.

X512 ★★★★★
()
Ответ на: комментарий от X512

Ныне наконец то понял - «Нет программиста, нет проблем».
ЯП высокого уровня нужно разрабатывать, а не а-ля Си. ...
Все проблемы типа программирования в Си уйдут, но дурака не знаю как победить.

https://studfile.net/preview/2262220/page:17/ 8.2.1 Сегментация с использованием страниц: multics

Почитаю.

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 5)
Ответ на: комментарий от X512

В таком случае равенство указателей само по себе ничего не значит

Конечно, ведь нельзя сравнивать указатели на два разных объекта, особенно если это указатели с разными типами, исключение это прямое сравнение.

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 2)
Ответ на: комментарий от X512

Это не о том. То что хочет автор топика делается через аппаратные сегменты как это было в Multics или 16 битных версия Windows.

Кольца защиты как раз и использовались в 16 битных версия Windows.

https://www.thevista.ru/page14670-saga_o_windows_glava_vtoraya_chast_pervaya?... Сага о Windows. Глава вторая. Часть первая

Начало было положено в AT-286 (вроде так).

Forum0888
()
Последнее исправление: Forum0888 (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Конечно, ведь нельзя сравнивать указатели на два разных объекта, особенно если это указатели с разными типами,…

пример

union {
  int _int;
  bool _bool;
}

указатель на _int и _bool равны или нет?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Есть удобный сайт https://en.cppreference.com/w/c/language/operator_comparison

If lhs and rhs are expressions of pointer type, they must be both pointers to objects of compatible types, except that qualifications of the pointed-to objects are ignored.

...

* pointers to members of the same union compare equal

...

all other pointer comparisons invoke undefined behavior

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от X512

PT_LOAD The array element specifies a loadable segment

Я не знаю правильный нейминг, мне кажется ты придираешься к словам, либо я чего то не понимаю.

Я совсем недавно добавлял генерацию elf в свой компилятор, так что как оно работает я понимаю, а с названиями я могу запутаться, тем более секция и сегмент это слова которые переставляют друг с другом...

MOPKOBKA ★★★★★
() автор топика
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA
  • pointers to members of the same union compare equal

о чем и речь. поэтому твоя фраза - неправильна

Конечно, ведь нельзя сравнивать указатели на два разных объекта, особенно если это указатели с разными типами, исключение это прямое сравнение.

кстати ее смысл вообще непонятен. разговор шел о сегментной модели и равенстве физических адресов. а ты перевел разговор на оператор равенства языка си, поведение которого определяется правилами совместимости типов

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)