Libseccomp: ERROR: gen_bpf_generate () no maneja correctamente la falla

Creado en 28 may. 2020  ·  15Comentarios  ·  Fuente: seccomp/libseccomp

Hola,

En primer lugar, gracias por libseccomp: lo hemos estado usando felizmente en producción durante varios años y no hemos tenido ningún problema (hasta ahora). No estoy seguro de si esto es un error en nuestro código, un malentendido de la documentación o algo más, pero pasé el mes pasado tratando de rastrear esto en vano.

Recientemente actualizamos los paquetes en nuestros contenedores Docker, que incluyeron una actualización de libseccomp 2.3.3 (versión en repositorios estables de Debian) a 2.4.3. Hubo otros paquetes del sistema que también se actualizaron, pero no los registré. Nuestro kernel no se actualizó y es la versión 4.19.0-8-amd64.

Usamos SCMP_ACT_TRACE , y construimos un filtro que consta solo de reglas SCMP_ACT_ALLOW que se agregan usando números de llamada al sistema nativos, en lugar de los pseudo-números de libseccomp. Bifurcamos un proceso auxiliar de 64 bits que construye y carga el filtro seccomp antes de exec -ing otro binario de 64 bits.

Como referencia, esta es la totalidad de nuestra rutina de inicialización seccomp, utilizando una verificación de errores similar a la de la página de manual seccomp_rule_add .

Sin embargo, nuestra llamada a seccomp_load ha comenzado a devolver -EINVAL , del orden de magnitud de 1 / 100.000 inicializaciones de proceso. (No poder reproducirlo de manera confiable ha hecho que la depuración sea tediosa). No hubo cambios en el código de nuestra aplicación durante este tiempo. Las llamadas al sistema agregadas al filtro son idénticas en todas las ejecuciones.

¿Alguna idea sobre lo que podría estar saliendo mal (o incluso cómo profundizar más en lo que va mal), o si esto se espera de alguna manera? No hay muchas partes móviles dinámicas y no pude encontrar nada en la documentación sobre por qué podría estar sucediendo esto.

bug prioritmedium

Comentario más útil

Desafortunadamente, todavía no. Después de agregar un parche a seccomp_export_pfc , se ha guardado silencio. Ayer envié ese parche a todas nuestras máquinas virtuales (en lugar de solo una de prueba) con la esperanza de capturar el problema cuando finalmente ocurra.

Me parece extraño el silencio, pero por ahora lo atribuyo a una coincidencia, ya que toda la lógica de depuración / exportación ocurre _después_ de fallar seccomp_load , por lo que no debería afectar la falla en sí.

Todos 15 comentarios

Hola @Xyene ,

No hay muchos lugares que devuelvan -EINVAL en la ruta del código seccomp_load (). Basado en un examen rápido del código libseccomp v2.4.3, parece que se debe a un scmp_filter_ctx inválido o al kernel quejándose de la llamada prctl(...) que carga el filtro.

Teniendo en cuenta que v2.4.3 generalmente funciona, y no ha cambiado su kernel, parece dudoso que la llamada prctl(...) sea ​​la causa que nos lleva a un contexto de filtro no válido. ¿Ha notado algún otro comportamiento extraño en su programa desde la actualización? Me pregunto si hay un problema de corrupción de memoria en otro lugar que está causando el problema.

Si bien siempre es posible que la falla esté en libseccomp, ejecutamos cada versión a través de una serie de verificaciones que incluyen ejecuciones de valgrind para todas nuestras pruebas de regresión, así como análisis estático usando clang y Coverity.

Además, aunque esto no ayuda para la v2.4.3, una de las mejoras a las que nos dirigimos para la versión v2.5.0 casi lista es la mejora de la documentación y el manejo de los códigos de error.

Recientemente actualizamos los paquetes en nuestros contenedores Docker, que incluyeron una actualización de libseccomp 2.3.3 (versión en repositorios estables de Debian) a 2.4.3. Hubo otros paquetes del sistema que también se actualizaron, pero no los registré. Nuestro kernel no se actualizó y es la versión 4.19.0-8-amd64.

Gracias por verificar que su código y el kernel subyacente no han cambiado. Eso debería ayudar a localizar el problema.

Como referencia, esta es la totalidad de nuestra rutina de inicialización seccomp, utilizando una verificación de errores similar a la de la página de manual seccomp_rule_add .

Tu filtro me parece razonable.

¿Alguna idea sobre lo que podría estar saliendo mal (o incluso cómo profundizar más en lo que va mal), o si esto se espera de alguna manera? No hay muchas partes móviles dinámicas y no pude encontrar nada en la documentación sobre por qué podría estar sucediendo esto.

Revisé el código v2.4.3 seccomp_load() , y creo que solo hay dos lugares donde libseccomp genera un código de retorno de -EINVAL :

Los dos errores anteriores son causados ​​efectivamente por un filtro no válido. Eso me parece poco probable basado en su código de filtro.

Vale la pena señalar que el valor de retorno predeterminado del kernel en seccomp_set_mode_filter() es -EINVAL , por lo que es posible que algo más en el sistema haya cambiado, lo que nos lleva a caer en ese camino. Mencionas que estás ejecutando en Docker; ¿Está deshabilitando el filtro seccomp de Docker predeterminado?

Me sentiría tentado a agregar más depuración a su código dentro del if after seccomp_load() falla. Por ejemplo, podríamos generar el PFC y / o el BPF del propio filtro para verificar que parezca razonable. Consulte seccomp_export_pfc() y seccomp_export_bpf() .

Revisé el código v2.4.3 seccomp_load() , y creo que solo hay dos lugares donde libseccomp genera un código de retorno de -EINVAL :

Tenga en cuenta que cualquier falla encontrada en gen_bpf_generate(...) , o por debajo, se combina efectivamente en -ENOMEM por sys_filter_load(...) en src / system.c: 267 .

¡Odio volver a la "corrupción de la memoria"! muy rápido, pero parece que ese puede ser el caso aquí.

¡Gracias por las respuestas rápidas y detalladas! Han generado varias vías de exploración: mild_smiling_face:

¿Ha notado algún otro comportamiento extraño en su programa desde la actualización? Me pregunto si hay un problema de corrupción de memoria en otro lugar que está causando el problema.

No, solo esto. Nuestras pruebas de unidad e integración continúan pasando, y aparte de este EINVAL muy raro, no se registran errores en prod. Esto ciertamente lo hace desconcertante; También sospeché corrupción de la memoria, pero no he podido encontrar ninguna evidencia que lo respalde :ligeramente_frowning_face:

Para un poco más de contexto:

  • el programa es una aplicación de Python que llama a C ++ a través de Cython (el GIL se mantiene durante este tiempo, por lo que Python no realiza asignaciones en otros subprocesos)
  • las bifurcaciones laterales de C ++, y en el niño configura el filtro seccomp antes de exec
  • todas las asignaciones de memoria que ocurren después de la bifurcación, antes de la ejecución son de libseccomp en sí, en seccomp_init etc.
  • hay exactamente 4 mallocs de matriz entre llamar al código C ++ y bifurcar, todos están en Cython y tienen rangos apropiados al escribirlos (la línea 435 llama al código seccomp previamente vinculado) - todas las demás asignaciones / escrituras / etc. están dentro del intérprete de Python

Mientras escribía esto, tuve una idea: he escuchado historias de terror acerca de que malloc no es seguro de usar después de la bifurcación, y tenemos algunas dentro de libseccomp. La aplicación de Python en sí _es_ multiproceso, pero siempre mantenemos el GIL mientras está en código nativo, por lo que esto debería ser seguro (?). Sin embargo, solo he oído hablar de interbloqueos que ocurren a través de malloc-after-fork. (Supongo que esto hace que el siguiente orden del día se mueva seccomp_init et al. Antes de la bifurcación, solo llamando a seccomp_load post-fork y viendo si los errores siguen ocurriendo).

Me sentiría tentado a agregar más depuración a su código dentro del if after seccomp_load () falla.

¡Gracias por la sugerencia! Agregué una llamada a seccomp_export_pfc , además de descargar el contenido de la entrada al filtro ( config->syscall_whitelist ). Haré un seguimiento la próxima vez que esto falle.

Hola @Xyene : ya que ha pasado aproximadamente una semana, solo quería verificar y ver si hay algo nuevo que hayas encontrado.

Desafortunadamente, todavía no. Después de agregar un parche a seccomp_export_pfc , se ha guardado silencio. Ayer envié ese parche a todas nuestras máquinas virtuales (en lugar de solo una de prueba) con la esperanza de capturar el problema cuando finalmente ocurra.

Me parece extraño el silencio, pero por ahora lo atribuyo a una coincidencia, ya que toda la lógica de depuración / exportación ocurre _después_ de fallar seccomp_load , por lo que no debería afectar la falla en sí.

¡Progreso!

Resulta que la razón por la que ha estado en silencio es porque seccomp_export_bpf estaba segfaulting (¿debería, si se llama después de seccomp_load ?), Y eso se informó en otro lugar y no donde estaba buscando fallas de seccomp. Más importante aún, me encontré con un caso en el que puedo reproducir de manera confiable el problema en ~ 150 invocaciones, por lo que con un poco de trabajo de plomería debería poder extraer algunos volcados de núcleo.

Muy bien, saqué un volcado de núcleo y este fue el rastro: https://gist.github.com/Xyene/920f1cb098784a031f53c66a2f49d167

Esto fue un poco sospechoso, ya que está fallando dentro de la rutina realloc jemalloc. Además, el uso de glibc malloc resuelve el problema (desafortunadamente, no es una opción a largo plazo en este caso debido a problemas de fragmentación).

A continuación, extraje jemalloc, lo compilé con -O0 y símbolos de depuración, y volví a ejecutar la reproducción. ¡Esta vez se estrelló en seccomp_load , en lugar de después! He subido ese rastro aquí: https://gist.github.com/Xyene/5da56168bcea337da85b2cd30704d12e

Un fragmento de ese rastro:

#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

Al buscar en jemalloc, parece que 0x5a se usa para marcar los bytes libres como libres , con la intención específica de bloquear el código que intenta liberar algo que ya se ha liberado.

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

Pero esto no tiene mucho sentido ya que la duración del programa es solo el cuerpo de sys_filter_load :

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

Creo que he detectado al menos un problema. En gen_bpf_generate ;

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

state.bpf = prgm siempre que zmalloc no falle. A continuación, se llama a _gen_bpf_build_bpf y, basándose en su rc , state.bpf se establece en NULL .

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

Teniendo en cuenta el caso en el que rc != 0 , state.bpf todavía se establece en prgm en el momento de la llamada a _state_release . Esto hará que se libere la memoria apuntada por prgm .

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

A continuación, gen_bpf_generate será return prgm , que a pesar de haber sido liberado, sigue siendo un puntero distinto de cero.

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

De vuelta en sys_filter_load , gen_bpf_generate devuelve, y prgm no es- NULL por lo que continúa.

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

Finalmente, al final de sys_filter_load , se llama a gen_bpf_release en el prgm ya libre.

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

Esto no aborda la preocupación de por qué _gen_bpf_build_bpf fallaría en primer lugar, pero parece algo malo que podría suceder si lo hiciera.

Editar: en realidad, parece que probablemente se corrigió como un efecto secundario de https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace.

Teniendo en cuenta el caso en el que rc! = 0, state.bpf todavía está configurado en prgm en el momento de la llamada a _state_release. Esto hará que se libere la memoria apuntada por prgm.

¡Ah, ja! ¡Buen partido @Xyene!

Creo que tenemos que arreglar esto más allá de 3a1d1c977065f204b96293cccfe7d3e5aa0d7ace, déjame pensar en esto por un minuto ... No creo que la solución sea demasiado difícil ... y ver si puedo llegar a un PR.

Creo que tenemos que arreglar esto más allá de 3a1d1c9, déjame pensar en esto por un minuto ... No creo que la solución sea demasiado difícil ... y ver si puedo llegar a un PR.

Vaya, estaba mirando el código antiguo cuando escribí eso; sí, creo que 3a1d1c9 nos arregla esto, pero necesitaremos un parche para la rama de la versión 2.4. Trabajaré en eso ahora.

_ (Meta: Continuaré actualizando este mensaje con mis hallazgos a medida que avanzo, así que tengo un lugar para escribirlos sin enviarles correos electrónicos no deseados:) _

Muy bien, de vuelta en 2.4.3 con el parche aplicado, pude sacar el filtro que estaba fallando: enlace .

La causa informada ahora es ENOMEM lugar de EINVAL , que supongo que se espera dado que _gen_bpf_build_bpf está fallando y devuelve un programa NULL . Sin embargo, el PFC se imprime bien. Modificar el código seccomp para informar el valor de retorno de _gen_bpf_build_bpf muestra EFAULT como la causa.

Como un truco rápido, ejecuté :%s/return -EFAULT/abort() sobre src/gen_bpf.c , y pude extraer este rastro de pila:

Stacktrace de 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

Eso corresponde con la línea 1943:

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

Dada la naturaleza del reemplazo, creo que podemos excluir cualquier EFAULT en cualquier función auxiliar, ya que se habrían abortado primero.

Después de esto, intenté reproducir lo mismo con HEAD; todavía sucede. A continuación, %s:/goto build_bpf_free_blks/abort() y repita. La causa fue:

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

Afortunadamente, esta función era corta y solo tenía un puñado de puntos de falla. Otra ronda de inserciones abort más tarde;

Rastro

(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

Entonces es realloc fallando nuevamente, y _bpf_append_blk devuelve -ENOMEM que se enmascara por _gen_bpf_build_bpf y se convierte en -EFAULT . Esto no es un gran problema, pero como dijiste que un mejor informe de errores es un objetivo de 2.5, pensé que lo mencionaría ya que parece dentro del alcance:

Algunos pinchando con 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

Este realmente comienza a verse como una falla en el asignador ...

Ajá, esta historia finalmente ha llegado a su _ emocionante_ conclusión: descubrí lo que ha estado sucediendo y verifiqué una solución: levemente_sonriente_cara:

Como podría ser una historia interesante, aquí está:

El proceso principal que bifurca al trabajador generalmente se encuentra en ~ 80mb RSS. Después de que se bifurca, restringe el uso de la memoria a través de rlimit , a veces a 64 MB. Esto lo coloca en una posición en la que su uso de memoria actual excede su límite, pero esto está permitido por rlimit . La mayoría de las veces, el asignador de memoria tendrá suficiente memoria libre disponible para dar servicio a las rutinas de inicialización de libseccomp sin solicitar más del kernel. Pero cuando _no lo hace_, y necesita solicitar espacio para una arena adicional o algo así, el kernel no lo proporcionará ya que el proceso ya está por encima de su límite.

En 2.4.3, esta falla en la obtención de memoria se manifestó en EINVAL y un doble libre. En la publicación maestra: https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace , se informa EFAULT su lugar. Con https://github.com/seccomp/libseccomp/pull/257 aplicado, ENOMEM se informa correctamente.

La razón por la que esto sucede tan raramente se vuelve obvia: depende completamente de si el asignador tiene suficiente memoria para construir el programa BPF sin solicitar más del kernel. El asignador de glibc es más flexible acerca de permitir que se acumule la fragmentación, por lo que esto nunca sucedió con él en su lugar. jemalloc establece límites más estrictos y conduce a una mayor probabilidad de necesitar solicitar memoria durante seccomp_load , lo suficiente para notar las fallas resultantes, pero aún así es exasperante rastrear.

La solución, entonces, es simplemente mover todas las llamadas setrlimit a _after_ seccomp_load . Al hacerlo, realloc ya no falla en _bpf_append_blk y el filtro se carga correctamente. Esto significa que el filtro debe permitir setrlimit , pero en mi caso esto fue aceptable. De manera más general, creo que este problema se resolvería con algo como https://github.com/seccomp/libseccomp/issues/123.

@pcmoore , @drakenclimber : ¡gracias de nuevo por toda su ayuda para depurar este problema! Me alegro de poder dejarlo atrás ahora, pero sus consejos fueron invaluables para llegar allí: smiley:

¿Fue útil esta página
0 / 5 - 0 calificaciones