В этой статье я расскажу, как можно настроить сохранение дампов в Linux при падении процессов, такие дампы называют core dumps.
Введение
При работе в Linux у вас запускается множество процессов, но иногда какой-нибудь процесс может начать падать. Или какая-нибудь программа может не запускаться. Иногда разработчики для анализа таких ситуаций просят выслать им дамп ядра (core dumps) относящийся к падению их программы.
Вообще core dumps никак не связан с ядром Linux. Термин «Ядро» в этом случае относится к старой памяти на магнитных сердечниках из старых систем (magnetic core memory). Хотя такая память уже не используется, но термин core dumps всё еще употребляется.
В core dumps сохраняется память сбойного процесса при его падении, а также некоторая служебная информация.
Ограничение на размер дампа
Самое главное ограничение, которое говорит системе, делать дамп упавшего процесса или не делать — это ограничение максимального размера дампа. Если это ограничение равняется 0 — то дамп не будет делаться совсем. Также вы можете ограничить максимальный размер дампа произвольным числом в байтах, или отключить лимит вовсе.
Посмотреть текущее ограничение для своего пользователя и своей оболочки можно выполнив команду:
$ ulimit -S -c 0
В выводе у меня 0 — это означает что создание дампов под моим пользователем и в моём окружении невозможно.
Командой выше мы смотрели мягкое (soft) ограничение. Это фактическое ограничение, которое влияет на процессы.
Есть ещё жесткое ограничение (hard), его может поменять только root пользователь. Давайте посмотрим как сейчас нас ограничивает жёсткое ограничение:
$ ulimit -H -c unlimited
Из этого следует, что сейчас жёсткое ограничение нас вообще не ограничивает. Поэтому мы можем изменить для себя мягкое ограничение и разрешить создание дампов. Это делается командой:
$ ulimit -c unlimited
Проверим:
$ ulimit -S -c unlimited
Куда сохранять дампы ядра
Чтобы узнать, куда сейчас сохраняются дампы ядра, нужно прочитать файл /proc/sys/kernel/core_pattern:
*** Debian 11 *** $ cat /proc/sys/kernel/core_pattern core *** Ubuntu 22.04 *** $ cat /proc/sys/kernel/core_pattern |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E
Debian называет файлы дампов именем core и куда-то их сохраняет (если честно, я не выяснял куда).
Ubuntu через пайп передаёт дамп на обработку программе apport.
Вы можете временно (до перезагрузки) изменить путь и имя дампов задав свой шаблон, например таким способом:
$ sudo sysctl -w kernel.core_pattern=/var/crash/core.%u.%e.%p
Выше мы меняем параметр sysctl — kernel.core_pattern. И задаём ему значение состоящее из пути и имени файла, а также используем переменные:
- %u — имя пользователя (под каким пользователем работала упавшая программа);
- %e — имя программы;
- %p — номер процесса (pid).
Проверим что путь изменился. Затем нужно создать этот каталог, если его ещё не существует и убедиться что наш пользователь сможет в него писать.
*** Ubuntu 22.04 *** $ cat /proc/sys/kernel/core_pattern /var/crash/core.%u.%e.%p $ ls -ld /var/crash/ drwxrwxrwx 2 root root 4096 апр 21 01:01 /var/crash/ *** Debian *** $ cat /proc/sys/kernel/core_pattern /var/crash/core.%u.%e.%p $ ls -ld /var/crash/ ls: невозможно получить доступ к '/var/crash/': Нет такого файла или каталога $ sudo mkdir /var/crash/; sudo chmod 777 /var/crash/ $ ls -ld /var/crash/ drwxrwxrwx 2 root root 4096 мая 31 15:50 /var/crash/
Хорошо, путь мы поменяли и каталог по этому пути сделали доступным. Ограничение на размер дампов для своей оболочки тоже сняли.
Создаём сбойную программу
Для того чтобы проверить что дампы для нас уже выполняются нужно запустить программу, которая точно упадёт. Напишем эту программу сами:
$ nano crash.c int main() { return 1/0; }
Наша программа выполняет деление на 0 и это приводит к сбою.
Теперь нужно откомпилировать эту программу (возможно вам придется установить gcc):
$ gcc -o crash crash.c crash.c: In function ‘main’: crash.c:3:13: warning: division by zero [-Wdiv-by-zero] 3 | return 1/0; | ^
Даже компилятор показывает, что наша программа содержит ошибку деления на ноль.
Теперь запустим программу:
$ ./crash Floating point exception (core dumped)
Программа падает с ошибкой и видно что был сформирован core dumped. Если бы дампы ядра были отключены, этой надписи в скобках не появилось бы.
Посмотрим на наш файл дампа:
$ ls -l /var/crash/ total 120 -rw------- 1 alex alex 299008 мая 31 12:58 core.1000.crash.1397
Настройка создания дампов на постоянной основе
Проделанное выше вернётся к значениям по умолчанию после перезагрузки. Также остальные процессы, которые были запущены от имени других пользователей и в других оболочках не будут создавать дампы при падениях. Исправим это внеся изменения в конфигурационные файлы.
Ограничение на размер дампа
Настройка лимитов производится в конфиге /etc/security/limits.conf или лучше создать отдельный конфиг в каталоге /etc/security/limits.d/.
$ sudo nano /etc/security/limits.d/core.conf root hard core unlimited root soft core unlimited * hard core unlimited * soft core unlimited
Звездочка означает что это правило применимо ко всем пользователям в системе, кроме root. Для root пользователя нужно создавать отдельные правила.
Дальше идет тип ограничения. Soft — это мягкое ограничение, которое фактически ограничивает процессы. А hard — это верхняя граница для soft. Соответственно soft должно быть всегда меньше hard.
Core — это специальное ограничение для дампов ядра. Ограничивать лимитами можно многие параметры, но это выходит за рамки этой статьи.
Unlimited — означает, что мы не ограничиваем размер дампов. Здесь вы можете указать число в байтах, или 0 чтобы совсем выключить создание дампов.
Чтобы внесённые изменения применились к какому-нибудь процессу, этот процесс нужно перезагрузить.
Путь сохранения дампов
Отредактируйте конфиг /etc/sysctl.conf:
$ sudo nano /etc/sysctl.conf kernel.core_pattern=/var/crash/core.%u.%e.%p fs.suid_dumpable=2
Вторым параметром мы разрешаем программам имеющим бит Setuid тоже сохранять дампы при падениях.
Setuid – это бит разрешения, который позволяет пользователю запускать исполняемый файл с правами владельца этого файла. Другими словами, использование этого бита позволяет нам поднять привилегии пользователя в случае, если это необходимо.
Параметр fs.suid_dumpable для sysctl может принимать следующие значения:
- 0 – отключено;
- 1 – включено;
- 2 – включено с ограничениями. Делает дампы ядра доступными для чтения только пользователю root.
Чтобы применить изменения, выполните sysctl с ключом -p.
$ sudo sysctl -p kernel.core_pattern = /var/crash/core.%u.%e.%p fs.suid_dumpable = 2
Разрешаем сохранять дампы службам systemd
Хорошо, путь к созданию дампов мы указали и лимиты для всех пользователей убрали.
Но для сохранения дампов служб работающих в systemd, нам нужно настроить ещё один конфиг.
$ sudo nano /etc/systemd/system.conf DefaultLimitCORE=infinity
Чтобы применить изменения выполните следующие команды:
$ sudo systemctl daemon-reexec $ sudo systemctl restart nginx.service
И чтобы проверить применились ли наши изменения посмотрите информацию о лимитах для процесса nginx:
$ systemctl status nginx.service | grep 'Main PID' Main PID: 2099 (nginx) $ cat /proc/2099/limits | grep 'core' Max core file size unlimited unlimited bytes
В командах выше я вначале получаю PID основного процесса nginx, а затем использую его, чтобы посмотреть лимиты этого процесса.
Вот теперь nginx сможет сохранить дамп при падении.
Сохранения дампа при падении nginx
Чтобы имитировать падение nginx, убьём его главный процесс отправив сигнал SIGSEGV:
$ sudo kill -s SIGSEGV 2099
Проверим что дамп появился:
$ ls -l /var/crash/ total 1940 -rw------- 1 root root 2347008 мая 31 13:41 core.0.nginx.2099 -rw------- 1 alex alex 299008 мая 31 12:58 core.1000.crash.1397
Чтение дампов ядра
Чтобы понять что произошло нужно прочитать дамп ядра. Для этого используется утилита gdb (её нужно установить). Вначале указывается путь к программе, затем путь к дампу:
*** Дамп нашей сбойной программы *** $ gdb ./crash /var/crash/core.1000.crash.1397 Program terminated with signal SIGFPE, Arithmetic exception. Программа завершилась с сигналом SIGFPE, арифметическое исключение. *** Дамп процесса nginx *** $ sudo gdb /usr/sbin/nginx /var/crash/core.0.nginx.2099 Program terminated with signal SIGSEGV, Segmentation fault. Программа завершена с сигналом SIGSEGV, ошибка сегментации.
Вывод
Дампы ядра могут быть полезны для устранения неполадок, но могут стать причиной утечки конфиденциальных данных. По возможности отключайте дампы ядра и включайте их только тогда, когда это действительно необходимо. И проверяйте, надежно ли хранятся файлы, чтобы обычные пользователи не могли видеть их. И независимо от того, какой выбор вы сделали, всегда проверяйте, работает ли ваша конфигурация именно так, как вы ожидаете.