Как определяется адрес текстового раздела исполняемого файла PIE в Linux?
Сначала я попытался немного перепроектировать его:
printf '
#include <stdio.h>
int main() {
puts("hello world");
}
' > main.c
gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
readelf -s ./pie | grep -E 'main$'
gdb -batch -nh \
-ex 'set disable-randomization off' \
-ex 'start' -ex 'info line' \
-ex 'start' -ex 'info line' \
-ex 'set disable-randomization on' \
-ex 'start' -ex 'info line' \
-ex 'start' -ex 'info line' \
./pie \
;
Выход:
64: 000000000000063a 23 FUNC GLOBAL DEFAULT 14 main
Temporary breakpoint 1, main () at main.c:4
4 puts("hello world");
Line 4 of "main.c" starts at address 0x5575f5fd263e <main+4> and ends at 0x5575f5fd264f <main+21>.
Temporary breakpoint 2 at 0x5575f5fd263e: file main.c, line 4.
Temporary breakpoint 2, main () at main.c:4
4 puts("hello world");
Line 4 of "main.c" starts at address 0x55e3fbc9363e <main+4> and ends at 0x55e3fbc9364f <main+21>.
Temporary breakpoint 3 at 0x55e3fbc9363e: file main.c, line 4.
Temporary breakpoint 3, main () at main.c:4
4 puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.
Temporary breakpoint 4 at 0x55555555463e: file main.c, line 4.
Temporary breakpoint 4, main () at main.c:4
4 puts("hello world");
Line 4 of "main.c" starts at address 0x55555555463e <main+4> and ends at 0x55555555464f <main+21>.
что указывает на то, что это 0x555555554000
+ случайное смещение + 63e
,
Но затем я попытался выполнить поиск ядра Linux и исходного кода glibc для 555555554
и не было хитов.
Какая часть какого кода рассчитывает этот адрес?
Я столкнулся с этим, когда отвечал: что такое опция -fPIE для позиционно-независимых исполняемых файлов в gcc и ld?
1 ответ
Некоторый поиск в Интернете по адресу 0x555555554000 дает подсказки: были проблемы с ThreadSanitizer https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual
Q: Когда я запускаю программу, она говорит: FATAL: ThreadSanitizer не может отобразить теневую память (что-то отображается в 0x555555554000 < 0x7cf000000000). Что делать? Вам необходимо включить ASLR:
$ echo 2 >/proc/sys/kernel/randomize_va_space
Это может быть исправлено в будущих ядрах, см. https://bugzilla.kernel.org/show_bug.cgi?id=66721...
$ gdb -ex 'set disable-randomization off' --args ./a.out
и https://lwn.net/Articles/730120/ "Стабильные обновления ядра". Опубликовано 7 августа 2017 г. 20:40 UTC (понедельник) пользователем hmh (подписчик) https://marc.info/?t=150213704600001&r=1&w=2( https://patchwork.kernel.org/patch/9886105/, коммит c715b72c1ba4)
Перемещение базы PIE x86_64 и arm64 с 0x555555554000 на 0x000100000000 нарушило работу AddressSanitizer. Это частичный возврат:
- commit eab09532d400 ("binfmt_elf: использовать ELF_ET_DYN_BASE только для PIE") ( https://patchwork.kernel.org/patch/9807325/ https://lkml.org/lkml/2017/6/21/560)
- commit 02445990a96e("arm64: переместить ELF_ET_DYN_BASE в 4GB / 4MB") ( https://patchwork.kernel.org/patch/9807319/)
Возвращенный код был:
b/arch/arm64/include/asm/elf.h /* * This is the base location for PIE (ET_DYN with INTERP) loads. On - * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * 64-bit, this is above 4GB to leave the entire 32-bit address * space open for things that want to use the area for 32-bit pointers. */ -#define ELF_ET_DYN_BASE 0x100000000UL +#define ELF_ET_DYN_BASE (2 * TASK_SIZE_64 / 3) +++ b/arch/x86/include/asm/elf.h /* * This is the base location for PIE (ET_DYN with INTERP) loads. On - * 64-bit, this is raised to 4GB to leave the entire 32-bit address + * 64-bit, this is above 4GB to leave the entire 32-bit address * space open for things that want to use the area for 32-bit pointers. */ #define ELF_ET_DYN_BASE (mmap_is_ia32() ? 0x000400000UL : \ - 0x100000000UL) + (TASK_SIZE / 3 * 2))
Итак, 0x555555554000 связано сELF_ET_DYN_BASE
макрос ( указанный в fs / binfmt_elf.c дляET_DYN
как не рандомизированныйload_bias
) а для x86_64 и arm64 это как 2/3 TASK_SIZE. Когда нетCONFIG_X86_32
, x86_64 имеет TASK_SIZE 2^47 - одна страница вarch/x86/include/asm/processor.h
/*
* User space process size. 47bits minus one guard page. The guard
* page is necessary on Intel CPUs: if a SYSCALL instruction is at
* the highest possible canonical userspace address, then that
* syscall will enter the kernel with a non-canonical return
* address, and SYSRET will explode dangerously. We avoid this
* particular problem by preventing anything from being mapped
* at the maximum canonical address.
*/
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
Старые версии:
/*
* User space process size. 47bits minus one guard page.
*/
#define TASK_SIZE_MAX ((1UL << 47) - PAGE_SIZE)
Более новые версии также имеют поддержку 5 уровня с __VIRTUAL_MASK_SHIFT
56 бит-v4.17/source/arch/x86/include/asm/processor.h
(но не хотите использовать его до того, как он будет включен пользователем+ commit b569bab78d8d ".. Не все пространство пользователя готово для обработки широких адресов")).
Итак, 0x555555554000 округляется в меньшую сторону (поload_bias = ELF_PAGESTART(load_bias - vaddr);
, vaddr равно нулю) из формулы 2^47-1страница * 3/2(или 2^56 для более крупных систем):
$ echo 'obase=16; (2^47-4096)/3*2'| bc -q
555555554AAA
$ echo 'obase=16; (2^56-4096)/3*2'| bc -q
AAAAAAAAAAA000
Немного истории 2/3 * TASK_SIZE:
коммит 9b1bbf6ea9b2 "использовать ELF_ET_DYN_BASE только для PIE" имеет полезные комментарии: "Позиция ELF_ET_DYN_BASE изначально была предназначена для того, чтобы держать загрузчиков подальше от двоичных файлов ET_EXEC..."
Не переполняйте 32 бита с помощью 2*TASK_SIZE "[uml-user] [PATCH] x86, UML: исправьте целочисленное переполнение в ELF_ET_DYN_BASE", 2015 и "ARM: 8320/1: исправьте целочисленное переполнение в ELF_ET_DYN_BASE", 2015:
Почти все арки определяют ELF_ET_DYN_BASE как 2/3 TASK_SIZE. Хотя кажется, что некоторые архитектуры делают это неправильно. Проблема в том, что 2*TASK_SIZE может переполниться 32-битным, поэтому действительный ELF_ET_DYN_BASE становится неправильным. Исправьте это переполнение, разделив TASK_SIZE до умножения:
(TASK_SIZE / 3 * 2)
- То же самое в 4.x, 3.y, 2.6.z, (где находится архив git repo davej-history и в or.cz) 2.4.z,... добавлено в 2.1.54 от 06-Sep-1997
diff --git a/include/asm-i386/elf.h b/include/asm-i386/elf.h +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3)