Як реєструються забруднення канальних забруднень?


11

Прапор GCC -fstack-protector дозволяє використовувати канали стека для захисту від переповнення стека. Впродовж останніх років використання цього прапора за замовчуванням помітніше.

Якщо пакет складено з -fstack-protector, і ми переповнюємо буфер у програмі, ми, ймовірно, отримаємо помилку, таку як:

*** buffer overflow detected ***: /xxx/xxx terminated

Однак "хто" відповідає за ці повідомлення про помилки? Де ці повідомлення записуються? Чи підбирає цей демон системного журналу ці повідомлення?

Відповіді:


10

Розбиття стека виявляється libssp, що входить до складу gcc. Він дуже намагається вивести повідомлення в термінал, і лише в тому випадку, якщо це не вдається, він входить у системний журнал - тому на практиці ви побачите повідомлення про переповнення буфера в журналах для демонів і, можливо, програм GUI.

Як тільки він виводить своє повідомлення, libsspнамагається вийти з різних способів виходу, включаючи збій програми; це може виявити один із ненормальних реєстраторів виходу, але це не гарантується.


1
Дозвольте навести конкретний приклад як спосіб подальшого вивчення цього пояснення. Виберемо nginx для цього прикладу. Я склав nginx із каналами стеків. Коли я запускаю nginx, він запускає процес, але нічого не виводить в оболонку. Натомість будь-які повідомлення реєструються у кількох файлах журналу. Якщо nginx виявить розбиття стека, libsspвиведе своє повідомлення за допомогою виводу stderr, використовуваного nginx. Потім libsspможна спробувати вийти з процесу (або дочірнього процесу для nginx). Якщо програма "не потребує" збою програми, то ненормальні реєстратори виходу не підберуть це. Це правильне тлумачення?
aedcv

Не зовсім - це буде намагатися розбити додаток, використовуючи __builtin_trap()перший, а потім , якщо це не вдається, намагається спровокувати порушення сегмента, і тільки якщо це не вдається, виходячи зі статусом 127
Стівен Кітт

Друк частини повідомлень не має кращих гарантій успіху, ніж вихід за допомогою основного способу отримання (наприклад abort()).
maxschlepzig

7

Сучасні дистрибутиви Linux, такі як CentOS / Fedora , за замовчуванням створили демона , що керує збоями (наприклад, systemd-coredumpабо abortd).

Таким чином, коли ваша програма закінчується ненормально (segfault, uncaught виключення, аборт, незаконна інструкція тощо), ця подія реєструється та реєструється цим демоном. Таким чином, ви знайдете деякі повідомлення в системному журналі та, можливо, посилання на каталог з деякими додатковими деталями (наприклад, основний файл, журнали тощо).

Приклад

$ cat test_stack_protector.c 
#include <string.h>

int f(const char *q)
{
  char s[10];
  strcpy(s, q);
  return s[0] + s[1];
}

int main(int argc, char **argv)
{
  return f(argv[1]);
}

Збірка:

$ gcc -Wall -fstack-protector test_stack_protector.c -o test_stack_protector

Виконати:

$ ./test_stack_protector 'hello world'
*** stack smashing detected ***: ./test_stack_protector terminated
======= Backtrace: =========
/lib64/libc.so.6(+0x7c8dc)[0x7f885b4388dc]
/lib64/libc.so.6(__fortify_fail+0x37)[0x7f885b4dfaa7]
/lib64/libc.so.6(__fortify_fail+0x0)[0x7f885b4dfa70]
./test_stack_protector[0x400599]
./test_stack_protector[0x4005bd]
/lib64/libc.so.6(__libc_start_main+0xea)[0x7f885b3dc50a]
./test_stack_protector[0x40049a]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
00600000-00601000 r--p 00000000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
00601000-00602000 rw-p 00001000 00:28 1151979                            /home/juser/program/stackprotect/test_stack_protector
0067c000-0069d000 rw-p 00000000 00:00 0                                  [heap]
7f885b1a5000-7f885b1bb000 r-xp 00000000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b1bb000-7f885b3ba000 ---p 00016000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3ba000-7f885b3bb000 r--p 00015000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3bb000-7f885b3bc000 rw-p 00016000 00:28 1052100                    /usr/lib64/libgcc_s-7-20170915.so.1
7f885b3bc000-7f885b583000 r-xp 00000000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b583000-7f885b783000 ---p 001c7000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b783000-7f885b787000 r--p 001c7000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b787000-7f885b789000 rw-p 001cb000 00:28 945348                     /usr/lib64/libc-2.25.so
7f885b789000-7f885b78d000 rw-p 00000000 00:00 0 
7f885b78d000-7f885b7b4000 r-xp 00000000 00:28 945341                     /usr/lib64/ld-2.25.so
7f885b978000-7f885b97b000 rw-p 00000000 00:00 0 
7f885b9b0000-7f885b9b3000 rw-p 00000000 00:00 0 
7f885b9b3000-7f885b9b4000 r--p 00026000 00:28 945341                     /usr/lib64/ld-2.25.so
7f885b9b4000-7f885b9b6000 rw-p 00027000 00:28 945341                     /usr/lib64/ld-2.25.so
7ffc59966000-7ffc59987000 rw-p 00000000 00:00 0                          [stack]
7ffc5999c000-7ffc5999f000 r--p 00000000 00:00 0                          [vvar]
7ffc5999f000-7ffc599a1000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
zsh: abort (core dumped)  ./test_stack_protector 'hello world'

Статус виходу - 134, що становить 128 + 6, тобто 128 плюс номер сигналу переривання.

Системний журнал:

Oct 16 20:57:59 example.org audit[17645]: ANOM_ABEND auid=1000 uid=1000 gid=1000 ses=3 subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 pid=17645 comm="test_stack_prot" exe="/home/juser/program/stackprotect/test_stack_protector" sig=6 res=1
Oct 16 20:57:59 example.org systemd[1]: Started Process Core Dump (PID 17646/UID 0).
Oct 16 20:57:59 example.org audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=systemd-coredump@21-17646-0 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Oct 16 20:57:59 example.org systemd-coredump[17647]: Process 17645 (test_stack_prot) of user 1000 dumped core.

                           Stack trace of thread 17645:
                           #0  0x00007f885b3f269b raise (libc.so.6)
                           #1  0x00007f885b3f44a0 abort (libc.so.6)
                           #2  0x00007f885b4388e1 __libc_message (libc.so.6)
                           #3  0x00007f885b4dfaa7 __fortify_fail (libc.so.6)
                           #4  0x00007f885b4dfa70 __stack_chk_fail (libc.so.6)
                           #5  0x0000000000400599 f (test_stack_protector)
                           #6  0x00000000004005bd main (test_stack_protector)
                           #7  0x00007f885b3dc50a __libc_start_main (libc.so.6)
                           #8  0x000000000040049a _start (test_stack_protector)
Oct 16 20:57:59 example.org audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=systemd-coredump@21-17646-0 comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'
Oct 16 20:58:00 example.org abrt-notification[17696]: Process 17645 (test_stack_protector) crashed in __fortify_fail()

Це означає , що ви отримаєте вхід від auditdаудиту демона і в systemd-coredumpаварії обробника.

Щоб перевірити, чи налаштований демон, що керує збоями, ви можете перевірити /proc, наприклад:

$ cat /proc/sys/kernel/core_pattern
|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %e

(все тестовано на Fedora 26, x86-64)


1
Я дуже радий, що ви розмістили цей приклад. Канарці ставлять на місце gcc. (Будь ласка, виправте мене, якщо я помиляюся) Я припускаю, що те, що трапляється, є чимось на зразок: gcc ставить «додатковий код» у програму для реалізації функціональності канарок; під час виконання та перед поверненням функції перевіряється значення; якщо забруднене, програма виведе повідомлення "стек розбиття виявлено" та викликає помилку. Ця помилка виявляється ОС, розпізнає помилку сегментації та друкує опубліковані вами зворотні траси та карту пам'яті. Нарешті, ОС вбиває додаток, генерує основний дамп і
записується

@aedcv, це в значній мірі історія - якщо бути точніше: виклик коду, abort()котрий перевіряє стек, викликає сигнал переривання, тобто немає помилки сегментації. Це просто те, що обробники сигналів за замовчуванням для помилок переривання / сегментації тощо виконують ту саму дію: записуйте ядро ​​та вийдіть з процесу з нерівним нулем статусу виходу, який також кодує номер сигналу. Запис ядра виконується ядром, і його поведінка може бути налаштована через /proc/.../core_pattern. У наведеному вище прикладі налаштований помічник користувальницького простору і, таким чином, викликається. Ядро також запускає аудит.
maxschlepzig

@maxschlepzig це не зовсім abort(), використовується код SSP __builtin_trap()(але ефект той самий).
Стівен Кітт

1
@StephenKitt, добре, подивіться на слід стека у наведеному вище прикладі. Там ви чітко бачите, як abort()називається.
maxschlepzig

1
@maxschlepzig так, звичайно, але це деталізація щодо впровадження (код GCC використовує, __builtin_trap()щоб уникнути явної залежності від abort()). Інші дистрибутиви мають різні сліди стека.
Стівен Кітт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.