Несподіваний дозвіл exec з mmap при складанні файлів, що входять до проекту


94

Я з цим стукаю головою в стіну.

У моєму проекті, коли я виділяю пам'ять за mmapдопомогою відображення ( /proc/self/maps), видно, що це читабельна та виконувана область, незважаючи на те, що я запитував лише читабельну пам'ять.

Переглянувши strace (що виглядало добре) та інші налагодження, я зміг визначити єдине, що, здається, уникне цієї дивної проблеми: видалення файлів збірки з проекту та залишення лише чистого C. (що ?!)

Тож ось мій дивний приклад, я працюю над Ubunbtu 19.04 та gcc за замовчуванням.

Якщо ви компілюєте цільовий виконуваний файл з файлом ASM (який порожній), то mmapповертає читабельну та виконувану область, якщо ви будуєте без цього, вона веде себе правильно. Дивіться результат, /proc/self/mapsякий я вклав у свій приклад.

example.c

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

int main()
{
    void* p;
    p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);

    {
        FILE *f;
        char line[512], s_search[17];
        snprintf(s_search,16,"%lx",(long)p);
        f = fopen("/proc/self/maps","r");
        while (fgets(line,512,f))
        {
            if (strstr(line,s_search)) fputs(line,stderr);
        }

        fclose(f);
    }

    return 0;
}

example.s : це порожній файл!

Виходи

З ASM включеною версією

VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0 

Без включеної версії ASM

VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0 

5
Це серйозно дивно.
FUZ

6
Мені вдалося відтворити це лише за допомогою GCC (без CMake), тому я відредагував питання, щоб зробити приклад більш мінімальним.
Джозеф Сібл-

2
Можливо , пов'язані stackoverflow.com/questions/32730643 / ...
Саме Kuhmonen

Ви можете мати рацію, частина відповіді на нього повинна бути близько READ_IMPLIES_EXEC персони
Бен Хіршберг

Зберіть свої вихідні файли -Wa,--noexecstack.
jww

Відповіді:


90

У Linux є домен виконанняREAD_IMPLIES_EXEC , який називається , що призводить до того, що всі сторінки, виділені, PROT_READтакож мають бути надані PROT_EXEC. Ця програма покаже, чи включено це для себе:

#include <stdio.h>
#include <sys/personality.h>

int main(void) {
    printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
    return 0;
}

Якщо ви складете це разом із порожнім .sфайлом, ви побачите, що він увімкнено, але без нього він буде відключений. Початкове значення цього походить від метаінформації ELF у вашому двійковому файлі . Зробіть readelf -Wl example. Цей рядок ви побачите під час компіляції без порожнього .sфайлу:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10

Але цей, коли ви компілювали його:

  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

Примітка RWEзамість просто RW. Причиною цього є те, що лінкер передбачає, що для ваших асемблерних файлів потрібен read-implicit-exec, якщо прямо не сказано, що вони не роблять, і якщо будь-яка частина вашої програми вимагає read-imppts-exec, тоді вона включена для всієї вашої програми . Файли складання, які компілює GCC, повідомляють, що цього не потрібно, за допомогою цього рядка (це ви побачите, якщо компілювати -S):

        .section        .note.GNU-stack,"",@progbits

Помістіть цей рядок example.s, і він буде повідомляти лінкеру, що він теж не потрібен, і ваша програма працюватиме як слід.


13
Святе лайно, це дивний дефолт. Я думаю, що ланцюжок інструментів існував до noexec, і коли noexec за замовчуванням могло зламати речі. Тепер мені цікаво, як інші асемблери, такі як NASM / YASM, створюють .oфайли! Але в будь-якому випадку, я думаю, що це механізм, який gcc -zexecstackвикористовує, і чому він робить не просто стек, а все, що виконується.
Пітер Кордес

23
@Peter - Тому проекти, такі як Botan, Crypto ++ та OpenSSL, які використовують асемблер, додають -Wa,--noexecstack. Я думаю, що це дуже неприємний гострий край. Мовчазні втрати nx-стеків повинні бути вразливими до безпеки. Бінутільці повинні це виправити.
jww

14
@jww Це справді проблема безпеки, дивно, що про це ніхто не повідомляв раніше
Бен Гіршберг

4
+1, але ця відповідь була б набагато кращою, якби .note.GNU-stack,"",@progbitsбуло пояснено значення / логіку рядка - зараз це непрозоро, еквівалентно "ця магічна рядок символів викликає цей ефект", але рядок чітко виглядає, що має якусь семантика.
mtraceur

33

Як альтернатива зміні файлів збірки за допомогою варіантів директиви розділів для GNU, ви можете додати -Wa,--noexecstackдо командного рядка для складання файлів складання. Наприклад, подивіться, як я це роблю в муслі configure:

https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a

Я вважаю, що принаймні деякі версії clang з інтегрованим асемблером можуть вимагати передачі його як --noexecstack(без -Wa), тому ваш сценарій налаштування, мабуть, повинен перевірити обидва та побачити, що прийнято.

Ви також можете використовувати -Wl,-z,noexecstackчас (в LDFLAGS) посилання, щоб отримати той же результат. Недоліком цього є те, що це не допомагає, якщо ваш проект створює статичні ( .a) бібліотечні файли для використання іншим програмним забезпеченням, оскільки потім ви не контролюєте параметри часу зв’язку, коли його використовують інші програми.


1
Хм ... я не знав, що ти був багатим Фелкером, перш ніж читати цю публікацію. Чому ваше ім'я не відображається?
СС Енн
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.