Показаны сообщения с ярлыком kernel. Показать все сообщения
Показаны сообщения с ярлыком kernel. Показать все сообщения

пятница, 26 февраля 2010 г.

Материалы по Linux Crypto API

В этом сообщении буду собирать ссылки на ресурсы по Linux Crypto API и XFRM. Информации очень мало, а исходный код довольно многоуровневый.
The Linux Kernel Cryptographic API
Технология IpSec
Crypto API in kernel
Linux Crypto [Дальше]

пятница, 14 августа 2009 г.

mmap для нулевого адреса

Прочитал на lor статью про новую уязвимость. Прочитал, что в качестве идеи эксплоита лежит возможность выполнить код по нулевому виртуальному адресу. Тут-то я и задумался... Каким образом пользовательское приложение может это (сформировать код по 0 VA) сделать? В эксплоите был код:

mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0)

Этот код у меня в системе не работает... Начал смотреть дальше, и вот что выяснилось.

Здесь описывается тот факт, что для нужд wine (выполнения 16 битного кода) в ядре разрешается делать mmap на 0 адрес (/proc/sys/vm/mmap_min_addr). Самое интересное, что в статье утверждается, что в Ubuntu при установке wine эта возможность включается для системы в целом. Кроме того, в следствии особенностей архитектуры SELinux, по умолчанию, системы с включенным SELinux (как я понял) также разрешают пользователям делать mmap 0 адреса.

Интересно. :)

UPD: Кроме того, если в системе есть pulseaudio с установленным suid root, то эксплоит будет загружен через pulseaudio -L и mmap будет уже делаться от пользователя с euid == 0 (и выполнится успешно). Правда, в таком случае можно получить рута более простым способом -- так как мы и так уже получили привилегии администратора. Довольно нечестный прием. :) [Дальше]

четверг, 9 апреля 2009 г.

Применение специальных возможностей GCC в яде Linux

На www.ibm.com появилась статья, в чем-то перекликающаяся с моими постами:http://syslogblog.blogspot.com/2008/10/blog-post.html и http://syslogblog.blogspot.com/2009/01/gcc.html. Некоторые вещи, которые показались интересными:


Определение адреса, откуда была вызвана функция. level -- глубина стека.

void * __builtin_return_address( unsigned int level );


Предварительная выборка.

void __builtin_prefetch( const void *addr, int rw, int locality );

[Дальше]

четверг, 19 марта 2009 г.

Слежение за дисковыми операциями

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

echo "1" > /proc/sys/vm/block_dump

И смотреть в dmesg: dmesg -c. При этом желательно остановить syslog, так как запись в syslog будет появляться в dmesg вызывая цепную реакцию. :) [Дальше]

четверг, 15 января 2009 г.

Разочарования...

Что-то все больше разочарований. Сначала Debian, со своей параноидальной борьбой за чистоту рядов и бюрократией... А теперь вот, заглянул в ядро Ubuntu и что вижу?

Все патчи свалены в один файл. Причем один из патчей -- это совершенно не нужный мне AppArmor, затрагивающий vfs слой (то есть фактически, ломающий совместимость для потенциально интересных патчей). Раскрутить это монолитное безобразие очень тяжело и вряд-ли оправданно. В общем, делаю вывод, что большие дистрибутивы обладают довольно существенным недостатком -- инертностью. Когда/если у меня появится новый ноутбук буду пробовать ArchLinux и DragonFly BSD. Про rpm-based дистрибутивы речь, конечно, даже не идет -- это уже просто куча хлама. [Дальше]

вторник, 9 декабря 2008 г.

Что такое derived work или в чем несвобода gpl?

В тексте лицензии GPLv2 используется такое понятие как "derived". Если говорить конкретно, то речь идет о том, что если ваша программа есть порожденная работа от GPLv2 проекта, то и она должна быть выпущена под GPLv2. Но что считать порожденной работой?

Например, если я пишу игру на SDL, является ли код игры derived? Или у нас есть драйвер модема для Linux -- должен ли он быть открыт? Вопрос почти философский, и тем не менее (или -- тем более) требующий выяснения.

Наверное, самое простое и удачное правило (Linux: EXPORT_SYMBOL_GPL vs EXPORT_SYMBOL), выглядит так:
"if work A would not exist (or would be radically different) if work B did not exist, then A is a derivative work of B."

По-моему очень точно соответствует действительности. Драйвер устройства может быть в общем случае написан для любой системы. По крайней мере большая часть драйвера может быть независима от API ядра. В тоже время модуль к оконному менеджеру выпущенному под лицензией GPLv2, даже выделенный в отдельный проект -- должен быть выпущен под той-же лицензией.

Довольно давно, в коде ядра Linux некоторые из символов стали экспортироваться как EXPORT_SYMBOL_GPL, что означает возможность их использования только в GPL модулях. Похоже, такую идею можно назвать попыткой решить вопросы лицензионной политики программным способом. (Если используешь функцию/подсистему специфичную для ядра Linux -- то ты работаешь над GPL проектом.)

Вообще, интересно отметить, что здесь лицензия GPL оказывается не "просто свободной" лицензией (как часто привычно думается), а довольно прагматичным способом решить те задачи, которые поставил Столлман. Гильдия программистов -- пользователей лицензии GPLv2, имеют полную свободу модифицировать код проекта, но платят за это своей работой и что самое интересное, НЕсвободой освободить проект из под GPL лицензии и сделать его общественным достоянием в полном смысле, например, выпустив его под не-copyleft BSD-лицензией.

В тоже время выпустив проект под BSD-лицензией, мы можем получить GPL продукт на его основе, в следствии совместимости BSD с GPL.

Если вас также, как и меня, заинтересовали подобные вопросы, то рекомендую ознакомиться со статьей: Владимир Осинцев. Всегда ли General Public License – это свобода?
[Дальше]

суббота, 22 ноября 2008 г.

НЕ-модули безопасности (lsm)

Странная судьба у хорошего, казалось-бы, начинания - lsm. Моя работа связана с модификацией ядра и я, когда перешли к использованию ядра 2.6.xx, был очень рад использовать хуки lsm там, где это возможно. Потом, с каждой версией я с ужасом наблюдал как колбасится интерфейс lsm. Как уходят из него все больше и больше нужных кухов (например post_mknod нет, а socket_post_create есть) и как мечется мысль разработчиков в поисках "правильного" пути. А потом, потом они сказали что модули больше не модули. security: Convert LSM into a static interface. А потом убрали возможность дергать security функции из модулей. В общем, похоже теперь lsm годен только для SELinux и немногочисленных модулей в составе ядра... :( [Дальше]

Неизвестная известная snprintf

Довольно часто при работе с форматными строками на C используется функция snprintf. При этом, типичной является конструкция, например, следующего вида:

for (...) {
offset += snprintf(buf + offset, PAGE_SIZE - offset, " %02x", val);
...

Постепенно заполняем буфер форматированной строкой. При этом часто негласно принимается такое предположение, что функция вернет количество записанных в буфер байт (не считая последнего 0). Интересно, что это не всегда так.

Дело в том, что если почитать man по snprintf, то мы обнаружим, что в случае если размер буфера не достаточен для строки, то функция возвращает количество байт без последнего 0, которые БЫЛИ БЫ записаны, в случае, если БЫ размер буфера БЫЛ БЫ достаточен! То есть, если в приведенном выше примере произойдет отсечение результата, то на следующей итерации цикла мы полезем за границу буфера с отрицательным размером буфера (тип которого приведется к беззнаковому size_t)!

Интересно, что в реализации ядра Linux, функция vsnprintf содержит такую проверку:

if (unlikely((int) size < 0)) {
/* There can be only one.. */
static char warn = 1;
WARN_ON(warn);
warn = 0;
return 0;
}

То-есть, если в результате итераций в первом примере мы получаем отрицательный размер буфера, мы об этом узнаем... ;)

Кроме того, в ядре Linux есть реализация функции scnprintf, которая выглядит следующим образом:

int scnprintf(char * buf, size_t size, const char *fmt, ...)
{
va_list args;
int i;

va_start(args, fmt);
i = vsnprintf(buf, size, fmt, args);
va_end(args);
return (i >= size) ? (size - 1) : i;
}


Как видим, это та функция, которая всегда возвращает число записанных в буфер байт не считая 0 и -1 если размер буфера 0. Хотя в документации написано: "If size is <= 0 the function returns 0", и это мне не понятно, так как из кода следует, что все-таки это будет -1.

А вот другой пример контроля за размером буфера:

for (i = 0; i < npids; i++)
cnt += snprintf(buf + cnt, max(sz - cnt, 0), "%d\n", a[i]);

Правда типы sz и cnt в данном случае не size_t, а int, что не очень красиво.

Ну и, наконец, можно просто проверять накопленное смещение:

if ((offset += snprintf(...)) >= buffsize) {
/* ... error handling ... */
}


Приятной особенностью поведения функции snprintf является тот факт, что мы можем вычислять размер буфера для отформатированной строки за счет передачи нулевого размера буфера. Например:


sz = snprintf(NULL, 0, " ..... ", .... ) + 1;
ptr = malloc(sz);
snprintf(ptr, sz, " .... ", ... );


Ссылки: snprintf() confusion' 2004 by corbet
[Дальше]

суббота, 4 октября 2008 г.

Трюки в коде ядра Linux.

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

Двойное отрицание
Иногда можно встретить двойное логическое отрицание. Например:

#define likely(x)        __builtin_expect(!!(x), 1)

На самом деле, тут мы еще видим реализацию одного из способов оптимизации, но об этом в другой раз. Пример содержит двойное отрицание. Зачем?

Если x является логическим выражением, то двойное отрицание не нужно, но что будет если в коде мы напишем что-то вроде:

int counter=0x100;
/* ... */
if (likely(counter))

И тут становится понятно, что двойное отрицание выражения всегда дает булевую величину (0 или 1). Его можно назвать приведением к булевому типу, что довольно непривычно звучит в C.

Выравнивание
/* align addr on a size boundary - adjust address up/down if needed */
#define _ALIGN_UP(addr,size) (((addr)+((size)-1))&(~((size)-1)))
#define _ALIGN_DOWN(addr,size) ((addr)&(~((size)-1)))

Ну, тут все понятно.

Комментарии с помощью препроцессора
#if 0
/* XXX: let's do this when we verify it is OK */
if (ret & VM_FAULT_OOM)
ret = -ENOMEM;
#endif

Как видим, удобно для быстрого отключения/включения кода, особенно если внутри уже есть комментарии /* ... */.

Необычное сравнение с константой

if (NULL == siocb->scm)
siocb->scm = &tmp_scm;

Помогает избежать ошибок, когда вместо сравнения (==) по ошибке ставится знак присваивания (=).

Является ли значение степенью 2?

bool is_power_of_2(unsigned long n)
{
return (n != 0 && ((n & (n - 1)) == 0));
}


do { /*...*/ } while(0)

# define cap_clear(c) do { (c) = __cap_empty_set; } while (0)

Очень часто используется при определении макросов, широко известный трюк. Теперь раскрытие макроса может использоваться как вызов функции, так как при раскрытии код будет полностью размещен в своем блоке, который выполнится один раз (while(0)). Внутри блока можно определять переменные. Например:
#ifndef swap
#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
#endif


Использование goto

Можно долго говорить, что использование goto в C это плохо, но никакое правило не может быть универсальным. Иначе -- фарисейство или фанатизм... ;)
grep "goto" -R * | wc -l
в дереве исходников ядра дает результат больший чем 50 тысяч.

В большинстве случаев это попытка избежать большей вложенности кода при отработке ошибочных систуаций и освобождении ресурсов. Например:

static long do_sys_truncate(const char __user * path, loff_t length)
{
struct nameidata nd;
struct inode * inode;
int error;

error = -EINVAL;
if (length < 0) /* sorry, but loff_t says... */
goto out;
/* ... */
inode = nd.path.dentry->d_inode;

/* For directories it's -EISDIR, for other non-regulars - -EINVAL */
error = -EISDIR;
if (S_ISDIR(inode->i_mode))
goto dput_and_out;
/* ... */
error = vfs_permission(&nd, MAY_WRITE);
if (error)
goto mnt_drop_write_and_out;
/* ... */
put_write_and_out:
put_write_access(inode);
mnt_drop_write_and_out:
mnt_drop_write(nd.path.mnt);
dput_and_out:
path_put(&nd.path);
out:
return error;
}

Это гораздо проще, чем вложенные друг в друга блоки или излишнее разбиение на функции.

Возвращение кодов ошибок с типом "указатель".

Пример кода:

char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
/* ... */
}
return fd;
}

Здесь видно, что getname возвращает указатель, который тем-не менее, может содержать признак ошибки. Здесь, кстати, нужно быть осторожным! Так как это противоречит практике возвращать признак ошибки как NULL. Нужно всегда четко понимать, что возвращает функция ядра в случае ошибки: NULL или PTR_ERR. Например, если написать следующий код:

char *tmp = getname(filename);
if (!tmp)
return NULL;
/* ... do something .. */

Это уже уязвимость, хотя выглядит прилично. Если не знать что возвращает getname...

Реализация механизма крайне проста, и сама по-себе тянет на трюк.
#define MAX_ERRNO       4095
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

static inline long IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}

Как видим, указатель содержит отрицательное значение ошибки. Таким образом, выброшенный диапазон адресов приходится на последние 4Kb виртуального адресного пространства, который функция никогда не вернет. Очевидно, что превращение кода ошибки в указатель -- это приведение типа:

static inline void *ERR_PTR(long error)
{
return (void *) error;
}

Вообще, все это дело терпимо для кода ядра Linux, но совсем нехорошо заниматься такими трюками в пользовательском (предположительно переносимом) коде, так-как все-таки это предполагает, что приведение long в void* и назад -- обратимы.

Вот и все на сегодня. :)
[Дальше]

Компиляция выбранного модуля в дереве сборки ядра Linux 2.6.

make -c <KERNEL TREE> SUBDIRS=<MODULES RELATIVE PATH> modules
Например. Весь каталог:
make -C /usr/src/linux-source-2.6.26 SUBDIRS=drivers/macintosh modules

Выбранный модуль:
make -C /usr/src/linux-source-2.6.26 SUBDIRS=drivers/macintosh therm_adt746x.ko 
Минимальный Makefile своего модуля:
obj-m    := our_module.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

[Дальше]

Архив блога