Обновить
151
0
Max Filippov @jcmvbkbc

low level freak

Отправить сообщение
Станьте спонсором шахтёрского/медицинского/учительского коллектива.
но не более 64KB за 1 раз

Это вы из проверки внутри if (error_code & PF_USER) сделали вывод? Но стек необязательно двигать командой entry. Можно тупо sub esp, 100500. Эта проверка о том, что обращаться к стеку ниже текущего указателя стека вообще-то нельзя, но если надо — то можно, но недалеко (64К).
mayorovp не так же. Вот кусок обработчика страничного сбоя для linux x86:

        vma = find_vma(mm, address);
        if (unlikely(!vma)) {
                bad_area(regs, error_code, address);
                return;
        }
        if (likely(vma->vm_start <= address))
                goto good_area;
        if (unlikely(!(vma->vm_flags & VM_GROWSDOWN))) {
                bad_area(regs, error_code, address);
                return;
        }
        if (error_code & PF_USER) {
                /*
                 * Accessing the stack below %sp is always a bug.
                 * The large cushion allows instructions like enter
                 * and pusha to work. ("enter $65535, $31" pushes
                 * 32 pointers and then decrements %sp by 65535.)
                 */
                if (unlikely(address + 65536 + 32 * sizeof(unsigned long) < regs->sp)) {
                        bad_area(regs, error_code, address);
                        return;
                }
        }
        if (unlikely(expand_stack(vma, address))) {
                bad_area(regs, error_code, address);
                return;
        }

В переводе на русский: для сбойного адреса ищется первая VMA, конечный адрес которой больше чем адрес сбоя. Если такая VMA найдена, и она расширяется вниз (VM_GROWSDOWN) — то это область стека. Попытаться её расширить функцией expand_stack.

Т.е. сбой по любому адресу в пустом месте перед стековой VMA вызовет попытку расширения стека.

Для ядра компилятор генерирует код по-другому?

Это не о ядерном коде вообще. Ядерные стеки имеют фиксированный размер и никогда не расширяются.
Принцип эксплуатации этого бородатого бага основан на увеличении объема стека без записи в него. Это делается по-разному в разных ОС, например, с помощью рекурсивного вызова процедуры, или многомегабайтными аргументами командной строки. Указатель стека перемещается на его начало (нижний адрес), стек резко наращивается, и – хоп, – указатель стека оказывается уже за сторожевой страницей. Доступа к самой сторожевой странице не происходит, ошибки нет. Получается, что область стека перекрывается с динамической областью какого-то процесса.

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


У вас здесь написана ерунда, потому что вы смешали в кучу пользовательский и ядерный стек.
Смотрите: сторожевая страница находится на границе пользовательского стека.

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


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

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


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

В статьях на которые приведены ссылки идёт речь о переполнении ядерного стека.
Зачем бинарник?
[fio]$ ./configure --help | grep static
--build-static          Build a static fio
Заметка про замену табов\пробелов — для этого есть специальные тулзы. Но можно и через универсальный tr,

в отличие от expand и unexpand, tr заменит 8 идущих подряд пробелов на 8 табов.
Обычно этого достаточно, чтобы компилятор начал сборку проекта

Этого достаточно, если Makefile проекта написан так, чтобы использовать переменную окружения CROSS_COMPILE. Вот как, например, Makefile ядра или U-Boot. С произвольным проектом это не работает.
Прошу прощения, нет больше вопросов. Вот что значит с утра не выпить кофе.
В этом месте википедии всё нормально написано. Тем не менее, начиная с заголовка HOW2 в вашей статье описывается не кросс-компиляция, а нативная компиляция внутри эмулятора.

Ещё один вопрос: если у вас есть gcc-linaro-5.3.1-2016.05-x86_64_arm-linux-gnueabihf, зачем танцы с ARM в QEMU?
Собственно, вот такая она, эта кросскомпиляция.

Собственно, это была вовсе и не кросскомпиляция.
тот же GCC хочет бутстрапа,

А если RTFM?
Building a native compiler
For a native build, the default configuration is to perform a 3-stage bootstrap of the compiler when ‘make’ is invoked.
It can be disabled with the --disable-bootstrap parameter to ‘configure'

Building a cross compiler
When building a cross compiler, it is not generally possible to do a 3-stage bootstrap of the compiler.
есть еще например LL/SC (которые по общности, в отличие от FAA, на равне с CAS).

LL/SC круче CAS, поскольку оно не страдает от проблемы ABA.
Я имею в виду вот что: над списком, второй пункт которого я процитировал, вы написали: «Из выше сказанного. В результате, что мы имеем:». Но до этого вы ни разу не упоминали детали реализации такого низкого уровня. Я не вижу, как этот пункт вытекает из сказанного вами выше.

Если вы хотели сказать, что для того, чтобы сделать CAS нужно загрузить старое значение из памяти, что даёт два обращения к памяти, а чтобы сделать xadd старое значение загружать не нужно, и обращение к памяти нужно только одно, то можно было так и сказать.

Ну и про грамматику уже вам сказали.
Если вы используете типичную нагрузку CAS идиомы, предполагая нормальный snoop-base когерентности кэша (подслушивание или snooping, это часто употребимая реализация когерентности в многоядерных системах), то нагрузка может вызывать read-to-share транзакцию, чтобы получить основную строку кэша в S или состояние E. CAS, который имеет эффективную память семантик в отношении протоколов когерентности кэшей, может вызвать другую транзакцию шины, чтобы обновить линию для M состояния. Таким образом, в самом худшем случае идиома может подвергнуть шину двум транзакциям, но реализация XADD будет стремиться провести передачу линии непосредственно в M состоянии. В процессе вы могли бы спекулировать значениями и получать короткий путь, который пытается получить «голый» CAS без предварительных загрузок. К тому же, это возможно для сложных реализаций процессора для выполнения согласованных спекуляций и целевого исследования линии в M состоянии. Наконец, в некоторых случаях можно успешно вставить инструкцию предвыборка-для-записи (PREFETCHW) до нагрузки, чтобы избежать транзакции обновления. Но этот подход должен быть применен с тщательностью, так как в некоторых случаях это может принести больше вреда, чем пользы. Учитывая все это, XADD, где это возможно, имеет преимущество.

Вы не могли бы перевести этот текст до конца на русский? Действительно ли сказанное здесь вытекает из вышесказанного?
А вот glibc (GNU C library) нельзя скомпоновать статически.

С чего бы это вдруг?
$ dpkg -L libc6-dev-i386 | grep '\.a'
/usr/lib32/libanl.a
/usr/lib32/libBrokenLocale.a
/usr/lib32/libc.a
/usr/lib32/libc_nonshared.a
/usr/lib32/libcrypt.a
/usr/lib32/libdl.a
/usr/lib32/libg.a
/usr/lib32/libieee.a
/usr/lib32/libm.a
/usr/lib32/libmcheck.a
/usr/lib32/libnsl.a
/usr/lib32/libresolv.a
/usr/lib32/librt.a
/usr/lib32/libutil.a
/usr/lib32/libpthread_nonshared.a
/usr/lib32/libpthread.a
не может быть в нормальной сети, где все по правилам, два пакета с одинаковыми 5-ю выше описанными данными!

Не может быть двух разных соединений. Пакетов-то может быть сколько угодно.
я показывал концепт.

Моё мнение — код, приводимый в качестве примера должен быть настолько близок к идеалу, насколько возможно, как по содержанию, так и по форме. Потому что, по сравнению с «продакшн кодом» который где-то просто работает, из этого кода существенно большее количество людей извлечёт что-то для себя, начиная от использованных приёмов и заканчивая скопипащенными строчками.
static ssize_t fw_device_read(struct file* filp, char __user *buffer, size_t length, loff_t* offset)
{
	printk("Reading from device, return total number of messages\n");
	return sprintf(buffer, "%u", accepted_num + dropped_num); 
}

При использовании printk строка формата должна начинаться с одного из макросов KERN_*, определяющих уровень важности сообщения, или с KERN_CONT, если нужно продолжить с последним использованным уровнем важности.

Но это несущественная ошибка, в отличие от sprintf в пользовательский буфер.

Что будет, если пользователь передаст в параметре buffer невалидный пользовательский адрес?
Или что будет, если пользователь передаст в параметре buffer виртуальный адрес где-нибудь в области данных ядра?
В первом случае приложение будет завершено с SIGSEGV и сообщением ядра о недопустимом обращении ядра к виртуальному адресу, хотя достаточно было бы одного SIGSEGV.
Во втором случае память ядра будет перезаписана вашей строкой, что вряд ли закончится хорошо.

Для безопасного копирования данных в/из пространства пользователя есть функции copy_to_user и copy_from_user, а так же strlen_user/strnlen_user/strncpy_from_user.

Кроме того, вы проигнорировали параметр length, из-за чего в следующем фрагменте пользовательского кода происходит переполнение буфера msg:

int all_msg() {
	char msg[1] = {0};
	int fd = open("/dev/device_fw", O_RDONLY);
	if (fd<0) {
		printf("Device access error, fd = %d\n", fd);
		return fd;
	}

	if(read(fd, &msg, 1)>0){	
		printf("Accepted packets number: %s\n", msg);
	} else {
		printf("Nothing to read\n");
	}

	close(fd);
	return 0;
}


static ssize_t fw_device_write(struct file *fp, const char *buff, size_t length, loff_t *ppos) 

Второй параметр этой функции тоже имеет атрибут __user.

Правильность использования атрибута __user можно проверить, передав параметр C=1 команде make собирающей модуль.
Да, верно. Перегрузка нужна для С++98. Тем более непонятно, в чём подвох.

Информация

В рейтинге
Не участвует
Откуда
Fremont, California, США
Зарегистрирован
Активность