Libseccomp: ОШИБКА: gen_bpf_generate () неправильно обрабатывает сбой

Созданный на 28 мая 2020  ·  15Комментарии  ·  Источник: seccomp/libseccomp

Здравствуй,

Во-первых, спасибо за libseccomp - мы успешно используем его в продакшене уже несколько лет и не сталкивались с какими-либо проблемами (до сих пор). Я не уверен, является ли это ошибкой в ​​нашем коде, неправильным пониманием документации или чем-то еще, но я потратил последний месяц, пытаясь отследить это, но безрезультатно.

Недавно мы обновили пакеты в наших контейнерах Docker, включая обновление с libseccomp 2.3.3 (версия в стабильных репозиториях Debian) до 2.4.3. Были и другие системные пакеты, которые также были обновлены, но я их не записывал. Наше ядро ​​не обновлялось и имеет версию 4.19.0-8-amd64.

Мы используем SCMP_ACT_TRACE и создаем фильтр, состоящий только из правил SCMP_ACT_ALLOW , которые добавляются с использованием собственных номеров системных вызовов, а не псевдо-чисел libseccomp. Мы отключаем 64-битный вспомогательный процесс, который создает и загружает фильтр seccomp перед exec -ингом другого 64-битного двоичного файла.

Для справки, это вся наша процедура инициализации seccomp с использованием проверки ошибок, аналогичной странице руководства

Однако наш вызов seccomp_load начал возвращать -EINVAL порядка 1/100 000 инициализаций процесса. (Отсутствие возможности надежного воспроизведения сделало эту отладку утомительной.) В это время в нашем приложении не было изменений кода. Системные вызовы, добавленные к фильтру, идентичны для всех запусков.

Есть идеи о том, что может пойти не так (или даже как глубже разобраться в том, что не так), или ожидается ли это каким-то образом? Не так много динамических движущихся частей, и я не смог найти в документации ничего о том, почему это может происходить.

bug prioritmedium

Самый полезный комментарий

К сожалению, пока нет. После добавления патча к seccomp_export_pfc молчал. Вчера я установил этот патч на все наши виртуальные машины (а не только на тестовый) в надежде зафиксировать проблему, когда она в конечном итоге возникнет.

Я нахожу молчание странным, но сейчас я списываю его на совпадение, поскольку вся логика отладки / экспорта происходит _после_ сбоя seccomp_load , поэтому это не должно влиять на сам сбой.

Все 15 Комментарий

Привет @Xyene!

В пути кода seccomp_load () не так много мест, которые возвращают -EINVAL. Основываясь на быстром изучении кода libseccomp v2.4.3, похоже, что это связано либо с недопустимым scmp_filter_ctx либо с жалобой ядра на вызов prctl(...) который загружает фильтр.

Учитывая, что v2.4.3 в целом работает, и вы не меняли свое ядро, кажется сомнительным, что вызов prctl(...) является причиной, которая приводит нас к недопустимому контексту фильтра. Заметили ли вы какое-либо другое странное поведение в вашей программе после обновления? Мне интересно, есть ли проблема с повреждением памяти где-либо еще, которая вызывает проблему.

Хотя всегда возможно, что ошибка связана с libseccomp, мы запускаем каждый выпуск через серию проверок, которые включают запуск valgrind для всех наших регрессионных тестов, а также статический анализ с использованием как clang, так и Coverity.

Кроме того, хотя это не помогает для v2.4.3, одно из улучшений, на которые мы нацелены для почти готовой версии v2.5.0, - это улучшенная документация и обработка кодов ошибок.

Недавно мы обновили пакеты в наших контейнерах Docker, включая обновление с libseccomp 2.3.3 (версия в стабильных репозиториях Debian) до 2.4.3. Были и другие системные пакеты, которые также были обновлены, но я их не записывал. Наше ядро ​​не обновлялось и имеет версию 4.19.0-8-amd64.

Спасибо за проверку того, что ваш код и базовое ядро ​​не изменились. Это должно помочь отследить проблему.

Для справки, это вся наша процедура инициализации seccomp с использованием проверки ошибок, аналогичной описанной на странице руководства seccomp_rule_add .

Ваш фильтр мне кажется разумным.

Есть идеи о том, что может пойти не так (или даже как глубже разобраться в том, что не так), или ожидается ли это каким-то образом? Не так много динамических движущихся частей, и я не смог найти в документации ничего о том, почему это может происходить.

Я просмотрел код v2.4.3 seccomp_load() и думаю, что есть только два места, где libseccomp генерирует код возврата -EINVAL :

Обе указанные выше ошибки фактически вызваны неправильным фильтром. Это кажется мне маловероятным, исходя из вашего кода фильтра.

Стоит отметить, что возвращаемое ядром умолчанию в seccomp_set_mode_filter() равно -EINVAL , поэтому возможно, что что-то еще в системе изменилось, что привело к тому, что мы упали по этому пути. Вы упомянули, что работаете в Docker; Вы отключаете фильтр Docker seccomp по умолчанию?

У меня возникнет соблазн добавить еще немного отладки в ваш код внутри if после сбоя seccomp_load() . Например, мы могли бы вывести PFC и / или BPF самого фильтра, чтобы убедиться, что он выглядит разумным. См. seccomp_export_pfc() и seccomp_export_bpf() .

Я просмотрел код v2.4.3 seccomp_load() и думаю, что есть только два места, где libseccomp генерирует код возврата -EINVAL :

Имейте в виду, что любые сбои, обнаруженные в gen_bpf_generate(...) или ниже, эффективно объединяются в -ENOMEM с помощью sys_filter_load(...) в src / system.c: 267 .

Ненавижу возвращаться к «порче памяти»! так быстро, но похоже, что это может иметь место здесь.

Спасибо за быстрые и подробные ответы! Они открыли для себя несколько направлений исследования.

Заметили ли вы какое-либо другое странное поведение в вашей программе после обновления? Мне интересно, есть ли проблема с повреждением памяти где-либо еще, которая вызывает проблему.

Нет, только это. Наши модульные и интеграционные тесты продолжают проходить, и, за исключением этого очень редкого EINVAL , в prod не регистрируются никакие ошибки. Это, конечно, вызывает недоумение; Я также подозревал повреждение памяти, но не смог найти никаких доказательств, подтверждающих это: слегка_frowning_face:

Для более подробного контекста:

  • программа представляет собой приложение Python, вызывающее некоторый C ++ через Cython (GIL удерживается в это время, поэтому Python не делает аллоки для других потоков)
  • боковые вилки C ++, а в дочернем элементе настраивает фильтр seccomp перед exec
  • все выделения памяти, которые происходят после форка, перед запуском, происходят из самой libseccomp, в seccomp_init и т. д.
  • между вызовом кода C ++ и разветвлением существует ровно 4 маллока массива , все они в Cython и имеют соответствующие диапазоны при записи в них (строка 435 вызывает ранее связанный код seccomp) - все остальные выделения / записи / и т. д. находятся в интерпретаторе Python

Набирая это, у меня возникла идея: я слышал ужасные истории о том, что malloc небезопасно использовать после разветвления, и у нас есть некоторые из них в самой libseccomp. Само приложение Python _ является многопоточным, но мы всегда храним GIL в нативном коде, так что это должно быть безопасно (?). Однако я слышал только о тупиках, возникающих через malloc-after-fork. (Я предполагаю, что это заставляет следующий порядок бизнеса перемещать seccomp_init и др. Перед форком, вызывая только seccomp_load post-fork и проверяя, продолжают ли возникать ошибки.)

У меня возникнет соблазн добавить дополнительную отладку в ваш код внутри if после сбоя seccomp_load ().

Спасибо за предложение! Я добавил вызов seccomp_export_pfc , а также выгрузил содержимое ввода в фильтр ( config->syscall_whitelist ). Я свяжусь с вами в следующий раз, когда это не удастся.

Привет, @Xyene, так как прошло около недели, я просто хотел проверить, есть ли что-нибудь новое, что вы нашли?

К сожалению, пока нет. После добавления патча к seccomp_export_pfc молчал. Вчера я установил этот патч на все наши виртуальные машины (а не только на тестовый) в надежде зафиксировать проблему, когда она в конечном итоге возникнет.

Я нахожу молчание странным, но сейчас я списываю его на совпадение, поскольку вся логика отладки / экспорта происходит _после_ сбоя seccomp_load , поэтому это не должно влиять на сам сбой.

Прогресс!

Оказывается, причина молчания в том, что в seccomp_export_bpf произошел сбой (если он был вызван после seccomp_load ?), И об этом сообщалось в другом месте, а не там, где я искал сбои seccomp. Что еще более важно, я столкнулся со случаем, когда я могу надежно воспроизвести проблему в ~ 150 вызовах, поэтому с некоторыми сантехническими работами я смогу извлечь некоторые дампы ядра.

Хорошо, я вытащил дамп ядра, и это был след: https://gist.github.com/Xyene/920f1cb098784a031f53c66a2f49d167

Это было немного подозрительно, поскольку происходит сбой внутри подпрограммы jemalloc realloc . Более того, использование glibc malloc вместо этого решает проблему (к сожалению, в данном случае это не долгосрочный вариант из-за проблем фрагментации).

Затем я вытащил jemalloc, скомпилировал его с помощью -O0 и отладочных символов и перезапустил воспроизведение. На этот раз он разбился в seccomp_load , а не после! Я загрузил эту трассировку здесь: https://gist.github.com/Xyene/5da56168bcea337da85b2cd30704d12e

Фрагмент этого следа:

#9  0x00007ff962698495 in free (ptr=0x5a5a5a5a5a5a5a5a) at src/jemalloc.c:2867
No locals.
#10 0x00007ff96062d087 in _program_free (prg=prg@entry=0x7ff95e963010) at gen_bpf.c:511
No locals.
#11 0x00007ff96062f605 in gen_bpf_release (program=program@entry=0x7ff95e963010) at gen_bpf.c:1986
No locals.
#12 0x00007ff96062c04f in sys_filter_load (col=col@entry=0x7ff95e9a5000) at system.c:293
        rc = -1
        prgm = 0x7ff95e963010
#13 0x00007ff96062b666 in seccomp_load (ctx=ctx@entry=0x7ff95e9a5000) at api.c:286
        col = 0x7ff95e9a5000

При поиске в jemalloc выясняется, что 0x5a используется для пометки свободных байтов как свободных с конкретным намерением сбой кода, который пытается освободить что-то, что уже было освобождено.

gen_bpf.c:511 в v2.4.3: https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L505 -L513

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

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L260 -L296

Думаю, я заметил по крайней мере одну проблему. В gen_bpf_generate ;

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1963 -L1966

state.bpf = prgm до тех пор, пока не завершится ошибка zmalloc . Затем вызывается _gen_bpf_build_bpf , и на основе его rc state.bpf устанавливается значение NULL .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1968 -L1971

Учитывая случай, когда rc != 0 , state.bpf по-прежнему имеет значение prgm во время вызова _state_release . Это приведет к освобождению памяти, на которую указывает prgm .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L539

Затем gen_bpf_generate будет return prgm , который, несмотря на то, что был освобожден, все еще является ненулевым указателем.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1971 -L1974

Вернувшись в sys_filter_load , gen_bpf_generate возвращается, а prgm равно NULL так что это продолжается.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L265 -L267

Наконец, в конце sys_filter_load вызывается gen_bpf_release для уже освобожденного prgm .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L292 -L295

Это не решает вопроса о том, почему _gen_bpf_build_bpf вообще не сработает, но похоже на что-то плохое, что могло бы случиться, если бы это произошло.

Изменить: на самом деле, похоже, что это, вероятно, было исправлено как побочный эффект https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace.

Учитывая случай, когда rc! = 0, state.bpf по-прежнему имеет значение prgm во время вызова _state_release. Это приведет к освобождению памяти, на которую указывает prgm.

Ага! Хороший улов @Xyene!

Я думаю, нам нужно исправить это помимо 3a1d1c977065f204b96293cccfe7d3e5aa0d7ace, позвольте мне подумать об этом на минутку ... Я не думаю, что исправление будет слишком сложным ... и посмотрю, смогу ли я придумать PR.

Я думаю, нам нужно исправить это помимо 3a1d1c9, позвольте мне подумать об этом на минуту ... Я не думаю, что исправление будет слишком сложным ... и посмотрю, смогу ли я придумать PR.

Ой, когда я писал это, я смотрел на старый код; да, я считаю, что 3a1d1c9 исправит это для нас, но нам понадобится патч для ветки release-2.4. Я сейчас над этим поработаю.

_ (Мета: Я буду продолжать обновлять это сообщение своими выводами по мере продвижения, так что у меня есть место, чтобы записать их, не рассылая вам спам по электронной почте, ребята :) _

Хорошо, вернувшись к 2.4.3 с примененным патчем, я смог вытащить фильтр, который не работал: ссылка .

Сообщенная причина теперь ENOMEM вместо EINVAL , что, я думаю, ожидается, учитывая, что _gen_bpf_build_bpf не работает и возвращает программу NULL . Однако PFC печатает нормально. При изменении кода seccomp для сообщения о возвращаемом значении _gen_bpf_build_bpf EFAULT в качестве причины указывается

В качестве быстрого взлома я запустил :%s/return -EFAULT/abort() поверх src/gen_bpf.c и смог извлечь эту трассировку стека:

EFAULT трассировка стека

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140084028365964, 140083248439464, 140083248438968, 140083248431088, 140084028368143, 28659884033, 140083965300736, 
            140083248439464, 140083248438968, 140083248431088, 140084028351031, 140084019988760, 140083248439624, 140083248431200, 140084028372597}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f67daa4d55b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f67d6f3eec0, sa_sigaction = 0x7f67d6f3eec0}, sa_mask = {__val = {140083965300736, 
              140083965300736, 0, 0, 140083248438968, 140083248438968, 140083248439464, 140083248431504, 140084028417173, 140083964793344, 
              140083965300736, 140083248431552, 140083994791895, 140083248431552, 140083994787642, 140083965300736}}, sa_flags = -1404894496, 
          sa_restorer = 0x0}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f67d8bfd455 in _gen_bpf_build_bpf (state=0x7f67ac4302e0, col=0x7f67d6f63040) at gen_bpf.c:1943
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f67d6fdcb60
        b_badarch = 0x7f67d6fd9000
        b_default = 0x7f67d6fd9060
        b_head = 0x7f67d6fda1a0
        b_tail = 0x7f67d6fd9000
        b_iter = 0x0
        b_new = 0x7f67d6fe3300
        b_jmp = 0x0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#3  0x00007f67d8bfd560 in gen_bpf_generate (col=0x7f67d6f63040) at gen_bpf.c:1971
        rc = 0
        state = {htbl = {0x0 <repeats 256 times>}, attr = 0x7f67d6f63044, bad_arch_hsh = 889798935, def_hsh = 742199527, arch = 0x7f67ac4301e0, 
          bpf = 0x7f67d6f64010}
        prgm = 0x7f67d6f64010
#4  0x00007f67d8bf64a7 in sys_filter_load (col=0x7f67d6f63040) at system.c:265
        rc = 32615
        prgm = 0x0
#5  0x00007f67d8bf4f10 in seccomp_load (ctx=0x7f67d6f63040) at api.c:287
        col = 0x7f67d6f63040

Это соответствует строке 1943:

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1935 -L1943

Учитывая характер замены, я думаю, что мы можем исключить любые EFAULT из любой вспомогательной функции, так как они сначала были бы прерваны.

После этого я попробовал воспроизвести то же самое с HEAD - все еще происходит. Далее %s:/goto build_bpf_free_blks/abort() и повторяем. Причина была:

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L2219 -L2220

К счастью, эта функция была короткой и имела лишь несколько точек отказа. Еще один раунд вставок abort позже;

След

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140050183343588, 0, 448, 140049402494880, 140049402509040, 140049402494832, 140050183342988, 140049402495088, 
            140049402509040, 140049402494896, 140050183343588, 4294967296, 140049402509040, 140049402509040, 140049402509040}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f5ff953055b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f5ff595d260, sa_sigaction = 0x7f5ff595d260}, sa_mask = {__val = {139642271694862, 
              140050119389792, 0, 0, 140049402502840, 0, 140049402503336, 140049402502888, 140049402502840, 112, 384, 140049402502840, 140050149861504, 
              140049402495328, 140050149857273, 392}}, sa_flags = 448, sa_restorer = 0x7f5ff595d240}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
        rc = -12
        i_new = 0x0
        i_iter = 0x7f5ff59fa178
        old_cnt = 48
        iter = 1
#3  0x00007f5ff76f3716 in _gen_bpf_build_bpf (state=0x7f5fcae302d0, col=0x7f5ff59c5000) at gen_bpf.c:2223
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f5ff59e1b60
        b_badarch = 0x7f5ff59de000
        b_default = 0x7f5ff59de060
        b_head = 0x7f5ff59df1a0
        b_tail = 0x7f5ff59de000
        b_iter = 0x7f5ff59df1a0
        b_new = 0x7f5ff59e8300
        b_jmp = 0x7f5ff59df0e0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#4  0x00007f5ff76f3874 in gen_bpf_generate (col=0x7f5ff59c5000, prgm_ptr=0x7f5fcae30b40) at gen_bpf.c:2270
        rc = 0
        state = {htbl = {0x0, 0x7f5ff593ef80, 0x7f5ff593efe0, 0x7f5ff593efc0, 0x0, 0x7f5ff595d000, 0x7f5ff593ef60, 0x7f5ff593ef00, 
            0x0 <repeats 248 times>}, attr = 0x7f5ff59c5004, bad_arch_hsh = 889798935, def_hsh = 742199527, bpf = 0x7f5ff5964010, 
          arch = 0x7f5fcae301c0, b_head = 0x7f5ff59e8300, b_tail = 0x7f5ff59de120, b_new = 0x7f5ff59e8300}
        prgm = <optimized out>
#5  0x00007f5ff76eb275 in sys_filter_load (col=0x7f5ff59c5000, rawrc=false) at system.c:307
        rc = 0
        prgm = 0x0
#6  0x00007f5ff76e9505 in seccomp_load (ctx=0x7f5ff59c5000) at api.c:386
        col = 0x7f5ff59c5000
        rawrc = false

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L449 -L452

Итак, realloc снова терпит неудачу, а _bpf_append_blk возвращает -ENOMEM который маскируется _gen_bpf_build_bpf и превращается в -EFAULT . Это не имеет большого значения, но, поскольку вы сказали, что улучшенная отчетность об ошибках - это цель 2,5, я подумал, что я упомянул бы об этом, поскольку это выглядит в рамках: немного_smiling_face:

Некоторые ковыряются в GDB:

(gdb) f 2
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
452         abort();
(gdb) info args
prg = 0x7f5ff5964010
blk = 0x7f5ff59df1a0
(gdb) print prg->blks
$4 = (bpf_instr_raw *) 0x7f5ff59fa000
(gdb) x/32bx &prg->blks
0x7f5ff5964018: 0x00    0xa0    0x9f    0xf5    0x5f    0x7f    0x00    0x00
0x7f5ff5964020: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964028: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964030: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
(gdb) print ((prg)->blk_cnt * sizeof(*((prg)->blks)))
$5 = 392
(gdb) print prg->blk_cnt
$6 = 49

Это действительно начинает выглядеть как сбой распределителя ...

Ага, эта история наконец-то достигла своего _влекательного_ завершения - я понял, что происходит, и проверил исправление: слегка_smiling_face:

Поскольку это может быть интересная история, вот она:

Основной процесс, который отделяет воркера, обычно занимает около 80 МБ RSS. После разветвления он ограничивает использование памяти через rlimit , иногда до 64 МБ. Это ставит его в положение, когда его текущее использование памяти превышает его предел, но это разрешено rlimit . В большинстве случаев у распределителя памяти будет достаточно свободной памяти для обслуживания процедур инициализации libseccomp без дополнительных запросов от ядра. Но когда это _не__ и ему нужно запросить место для дополнительной арены или чего-то еще, ядро ​​не предоставит его, поскольку процесс уже превысил свой предел.

В 2.4.3 эта ошибка при получении памяти проявлялась в EINVAL и двойном освобождении. В главном посте - https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace вместо этого сообщается EFAULT . При применении https://github.com/seccomp/libseccomp/pull/257 ENOMEM отображается правильно.

Причина, по которой это происходит так редко, становится очевидной: это полностью зависит от того, достаточно ли памяти у распределителя для создания программы BPF без дополнительных запросов от ядра. Распределитель glibc более свободен в разрешении нарастания фрагментации, поэтому с ним этого никогда не происходило. jemalloc устанавливает более жесткие границы и приводит к увеличению вероятности необходимости запрашивать память во время seccomp_load - достаточно, чтобы заметить возникающие в результате сбои, но по-прежнему раздражает отслеживание.

Тогда исправление состоит в том, чтобы просто переместить все вызовы setrlimit в _after_ seccomp_load . При этом realloc больше не дает сбой в _bpf_append_blk , и фильтр загружается успешно. Это означает, что фильтр должен разрешать setrlimit , но в моем случае это было приемлемо. В целом, я думаю, что эту проблему можно решить чем-то вроде https://github.com/seccomp/libseccomp/issues/123.

@pcmoore , @drakenclimber - еще раз спасибо за вашу помощь в устранении этой проблемы! Я рад, что могу оставить это позади, но ваши указатели оказались неоценимыми в достижении цели: smiley:

Эта ошибка была исправлена ​​фиксацией https://github.com/seccomp/libseccomp/commit/c0a6e6fd15f74c429a0b74e0dfd4de5a29aabebd

Была ли эта страница полезной?
0 / 5 - 0 рейтинги