Максимальный размер файла для файлов имеет не детерминированное влияние на процессы

Я запускаю собственное ядро ​​2.6.27, и я просто заметил, что основные файлы, созданные во время segfault, больше, чем ограничение размера жесткого ядра, установленное для процессов.

И что делает его более странным, так это то, что основной файл иногда усекается (но не до предела, установленного ulimit).

Например, это программа, которую я попытаюсь выполнить и сбой ниже:

int main(int argc, char **argv)
{
    // Get the hard and soft limit from command line
    struct rlimit new = {atoi(argv[1]), atoi(argv[1])};

    // Create some memory so as to beef up the core file size
    void *p = malloc(10 * 1024 * 1024);

    if (!p)
        return 1;

    if (setrlimit(RLIMIT_CORE, &new)) // Set the hard and soft limit
        return 2;                     // for core files produced by this
                                      // process

    while (1);

    free(p);
    return 0;
}

И вот выполнение:

Linux# ./a.out 1446462 &    ## Set hard and soft limit to ~1.4 MB
[1] 14802
Linux# ./a.out 1446462 &
[2] 14803
Linux# ./a.out 1446462 &
[3] 14804
Linux# ./a.out 1446462 &
[4] 14807

Linux# cat /proc/14802/limits | grep core
Max core file size        1446462              1446462              bytes

Linux# killall -QUIT a.out

Linux# ls -l
total 15708
-rwxr-xr-x 1 root root     4624 Aug  1 18:28 a.out
-rw------- 1 root root 12013568 Aug  1 18:39 core.14802         <=== truncated core
-rw------- 1 root root 12017664 Aug  1 18:39 core.14803
-rw------- 1 root root 12013568 Aug  1 18:39 core.14804         <=== truncated core
-rw------- 1 root root 12017664 Aug  1 18:39 core.14807
[1]   Quit                    (core dumped) ./a.out 1446462
[2]   Quit                    (core dumped) ./a.out 1446462
[3]   Quit                    (core dumped) ./a.out 1446462
[4]   Quit                    (core dumped) ./a.out 1446462

Так произошло много вещей. Я установил жесткий предел для каждого процесса примерно на 1,4 МБ.

  • Полученные файлы ядра значительно превышают это ограничение. Почему?
  • И 2 из 4 основных файлов, созданных, усекаются, но точно 4096 байты. Что здесь происходит?

Я знаю, что основной файл содержит, среди прочего, выделенную стек и память кучи. Разве это не должно быть довольно постоянным для такой простой программы (дайте или возьмите не более нескольких байтов), и, следовательно, создайте согласованное ядро ​​между несколькими экземплярами?

редактирует:

1 Запрошенный выход du

Linux# du core.*
1428    core.14802
1428    core.14803
1428    core.14804
1428    core.14807

Linux# du -b core.*
12013568    core.14802
12017664    core.14803
12013568    core.14804
12017664    core.14807

2 Добавление memset() после malloc() определило вещи в том, что основной файл теперь усечен до 1449984 (еще 3522 байтов за лимит).

Итак, почему раньше были ядра, что они содержали? Как бы то ни было, он не подвергался ограничениям процесса.

3 Новая программа также показывает интересное поведение:

Linux# ./a.out 12017664 &
[1] 26586
Linux# ./a.out 12017664 &
[2] 26589
Linux# ./a.out 12017664 &
[3] 26590
Linux# ./a.out 12017663 &        ## 1 byte smaller
[4] 26653
Linux# ./a.out 12017663 &        ## 1 byte smaller
[5] 26666
Linux# ./a.out 12017663 &        ## 1 byte smaller
[6] 26667

Linux# killall -QUIT a.out

Linux# ls -l
total ..
-rwxr-xr-x 1 root root     4742 Aug  1 19:47 a.out
-rw------- 1 root root 12017664 Aug  1 19:47 core.26586
-rw------- 1 root root 12017664 Aug  1 19:47 core.26589
-rw------- 1 root root 12017664 Aug  1 19:47 core.26590
-rw------- 1 root root  1994752 Aug  1 19:47 core.26653           <== ???
-rw------- 1 root root  9875456 Aug  1 19:47 core.26666           <== ???
-rw------- 1 root root  9707520 Aug  1 19:47 core.26667           <== ???
[1]   Quit                    (core dumped) ./a.out 12017664
[2]   Quit                    (core dumped) ./a.out 12017664
[3]   Quit                    (core dumped) ./a.out 12017664
[4]   Quit                    (core dumped) ./a.out 12017663
[5]   Quit                    (core dumped) ./a.out 12017663
[6]   Quit                    (core dumped) ./a.out 12017663

Ответы

Ответ 1

Реализация сброса ядра может быть найдена в fs/binfmt_elf.c. Я буду следовать коду в 3.12 и выше (он изменился с помощью commit 9b56d5438), но логика очень похожа.

Вначале код решает, сколько сбрасывать VMA (область виртуальной памяти) в vma_dump_size. Для анонимного VMA, такого как куча brk, он возвращает полный размер VMA. Во время этого шага основной предел не задействован.

Первая фаза записи дампа ядра затем записывает заголовок PT_LOAD для каждого VMA. Это в основном указатель, в котором говорится, где найти данные в остальной части файла ELF. Фактические данные записываются в цикле for и фактически являются второй фазой.

Во время второй фазы elf_core_dump многократно вызывает get_dump_page, чтобы получить указатель struct page для каждой страницы адресного пространства программы, которое необходимо сбросить. get_dump_page - общая функция полезности, найденная в mm/gup.c. Комментарий к get_dump_page полезен:

 * Returns NULL on any kind of failure - a hole must then be inserted into
 * the corefile, to preserve alignment with its headers; and also returns
 * NULL wherever the ZERO_PAGE, or an anonymous pte_none, has been found -
 * allowing a hole to be left in the corefile to save diskspace.

и фактически elf_core_dump вызывает функцию в fs/coredump.c (dump_seek в вашем ядре, dump_skip в 3.12+), если get_dump_page возвращает NULL. Эта функция вызывает lseek, чтобы оставить отверстие в дампе (фактически, поскольку это ядро, оно вызывает file->f_op->llseek непосредственно на указателе struct file). Основное отличие состоит в том, что dump_seek действительно не подчинялся ulimit, а новый dump_skip.

Что касается того, почему вторая программа имеет странное поведение, это, вероятно, связано с ASLR (рандомизация адресного пространства). Какой VMA усечен, зависит от относительного порядка VMA, который является случайным. Вы можете попробовать отключить его с помощью

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

и посмотрите, являются ли ваши результаты более однородными. Чтобы повторно использовать ASLR, используйте

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space