LINUX.ORG.RU

И еще раз про syscalls...

 kernel hacking, ,


0

3

Читал исходники ядра. Много думал...

А дело тут вот в чем. Как бы «всем хорошо известно» и по-умолчанию предполагалось, что ядро содержит массив указателей на системные вызовы sys_call_table.

Поколения куул-хацкеров и прочих честных разработчиков разработали 100500 примерно четыре метода разыскивания адреса этой таблицы в памяти и замены соответствующего сисколла на свой. И ладно бы только куул-хацкеры! Честные коммерсанты, которые по разным причинам не хотят открывать код своих ядерных модулей, делают то же самое. И все бы хорошо, но в коде ядра 5.15-107, например, мы видим (см. arch/x86/entry/syscall_64.c)

#define __SYSCALL(nr, sym) extern long __x64_##sym(const struct pt_regs *);
#include <asm/syscalls_64.h>
#undef __SYSCALL

#define __SYSCALL(nr, sym) __x64_##sym,

asmlinkage const sys_call_ptr_t sys_call_table[] = {
#include <asm/syscalls_64.h>
};

И все хорошо, как и ожидалось.

А в 5.15-158, к примеру, картинка уже другая:

/*
 * The sys_call_table[] is no longer used for system calls, but
 * kernel/trace/trace_syscalls.c still wants to know the system
 * call address.
 */
#define __SYSCALL(nr, sym) __x64_##sym,
const sys_call_ptr_t sys_call_table[] = {
#include <asm/syscalls_64.h>
};
#undef __SYSCALL

#define __SYSCALL(nr, sym) case nr: return __x64_##sym(regs);

long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
{
	switch (nr) {
	#include <asm/syscalls_64.h>
	default: return __x64_sys_ni_syscall(regs);
	}
};

Поясняю. Теперь вместо вызова по индексу в массиве имеем «тупо свитч». Кстати, этот переход между способами вызова сисколла не однократный. В 6.1 опять используется традиционная схема. Вот мне интересно, кто-нибудь кроме меня в этом деле ковырялся? Как нам теперь сисколлы-то перехватывать надежным способом?

И да, «писать свой LSM» не предлагать. Не потому, что хотим что-то скрыть, а потому, что не можем или не хотим поддерживать свой форк ядра. Да и у клиентов будет стоять тот линукс, который будет стоять. А LSMы нонче модулями не грузятся.

★★★★★

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

Я не до конца исследовал чередование старого и нового способа вызова сисколлов. Но это чередование не однократное. Типа в 6.2 подмена указателя в таблице работает, а в каких-то 6.5 — нет. Там как в анекдоте про милицейскую мигалку: «работает — не работает». Вот и хочу выяснить «историю вопроса».

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

Ftrace — это только предопределенные трейспоинты. Хочется получить управление вместо оригинального сисколла, что-то сделать, по результатам вернуть -EACCESS, или вызвать оригинальный сисколл. И после возврата оттуда опять что-то сделать.

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

Ftrace — это только предопределенные трейспоинты.

Нет, конечно.

Все, что вы написали, делается с помощью ftrace. Можно перехватить любую функцию, не только сисколл, если она начинается с __fentry__ Оригинальную функццию можно вызвать как до, так и после подменной. RTFM, в общем.

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

Тут уже без парсера х86-инструкций не обойтись, сложно. Хотя если код сисколлов редко меняется - можно только проверить что начало понятное, которое мы знаем как подменить, а иначе писать ошибку. Но это всё уже сложно, да.

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

Да вот не хотелось бы код хачить, хочется обойтись подменой адресов максимум. А то завтра случится какой-нибудь ARM, и чо, писать очередной дизассемблер? Не, патчить код в рантайме — это неправильно. Тут коллеги советуют таки ftrace, ну вот будем посмотреть.

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

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

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

Ну вот похоже, что так, но бекпорт получился какой-то очень точечный. Сейчас там «чересполосица». 5.15 кое-где работает по-старому, 6.2-сколько-то — тоже, 6.5 работает по-новому. Черрипикали они это дело по месту что-ли?

Портировать такое в LTS — это отстрелить дофига работающего кода. Как бы, о таком предупреждать надо. Судя по тегам коммита — это релиз-кандидаты ветки 6.9. Имеют право и не предупреждать. Типа в новостях к релизу напишем. Но вот портировать из релиз кандидатов в production-ветки --- ну это диверсия какая-то, чесслово.

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

Да многим чем он лучше. И кеши не портит, и адреса в таблице сисколлов бесполезно подменять становится. Я, в целом, одобряю подобную замену, но только «предупреждать надо было». Если сказано, что начиная с 6.9 «будет так», то оно и ладно, все подготовиться успеют. А вот «точечные бомбардировки» по ранним ядрам зачем? В той же ветке 5.15 патчлевелов больше сотни, и только часть задело, причем так не подряд, шрапнелью. Тот же коммерческий динамический мониторинг от Tripware на перехваченных сисколлах сидит, а он у кучи банков в продакшене.

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

Да, ftrace помог, спасибо. Но это тоже не универсальное решение, требует инструментирования ядра. Вы ж сами про __fentry__ писали... Как я понимаю, все современные generic ядра должным образом собраны, а вот что оно там в каком-нибудь RHELe будет, то одному Редхату ведомо.

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

Дело в том, что все современные промышленные системы бинарного патчинга ядра, kpatch/kgraft/livepatch (в RHEL, SLES, Ubuntu соответственно) работают поверх ftrace.))

Поэтому они все правильно собраны. Еще это более-менее гарантирует стабильность интерфейса ftrace: иначе вся эта развесистая редхатовско-сусевская инфраструктура live патчинга ядра рассыпется.

Еще на нем основаны некоторые механизмы eBFP. Я с трудом могу себе представить ядро, собранное без ftrace.

Кстати, я не просто так не стал советовать более высокоуровневые kpatch/kgraft/livepatch. Они предназначены для замены функций целиком, без повторного использования оригинальной функции. Можно извернутья и использвать kpatch через жопу, но это может сломаться в любой момент(уже ломалось). Так делать не надо.

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

«Не говори, подруга, у самой муж — пьяница» (с)

Да, на все версии не надефайнишься.

Ну вот у меня тоже хватает #if LINUX_VERSION_CODE больше/меньше KERNEL_VERSION(x,y,z)

На пару тысяч строк кода уже с десяток набирается.

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

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

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

Только ради каких-то мифических кешей?

скорее всего да..не ради усложнять жизнь кулхацкерам

но надо почитать всю драму с патчём, чтобы точно указать основную причину

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

Из описания коммита:

Yes, the conditional branches also have branch prediction, but the branch prediction is much more controlled, in that it just causes speculatively running the wrong system call (harmless), rather than speculatively running possibly wrong random less controlled code gadgets.

This doesn't mitigate other indirect calls, but the system call indirection is the first and most easily triggered case.

ZOMG TEH DRAMA! :)

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

Портировать такое в LTS — это отстрелить дофига работающего кода. Как бы, о таком предупреждать надо. Судя по тегам коммита — это релиз-кандидаты ветки 6.9. Имеют право и не предупреждать. Типа в новостях к релизу напишем. Но вот портировать из релиз кандидатов в production-ветки — ну это диверсия какая-то, чесслово.

Always has been. Вот, в 5.15.у суспенд сломали (опять).

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

Ну вот вызываете вы write, а оптимизатор такой "ой, ну скорее всего там сискол open, он чаще вызывается, спекулятивно дерну его. Ой, там write? Ну сорян, я уже отправил в ядро.

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

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

gns ★★★★★
() автор топика