Несподіваний дозвіл 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.