Libseccomp: ERROR: Manejo A2 roto por src / db.c Rework

Creado en 26 feb. 2018  ·  18Comentarios  ·  Fuente: seccomp/libseccomp

Para probar las mejoras de rendimiento de mi árbol binario propuesto, escribí un conjunto poco realista de reglas para read () y su argumento de tamaño de búfer (A2). Pero parece que la confirmación de reelaboración de src / db.c (ce3dda9a1) rompió el procesamiento de A2, al menos para este caso de prueba.

Antes de la confirmación de la reelaboración de la base de datos, una lectura como la siguiente - read(devzero_fd, buf, 8000) - devolvió -10 . Después de esta confirmación, ahora devuelve -5 .

Aquí está el código C que usé para generar mis tontas reglas read ():

        /* read */
        for (i = 5; i <= 12; i++) {
                rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(i), SCMP_SYS(read), 1,
                        SCMP_A2(SCMP_CMP_GT, 4 << i));
                if (rc < 0) {
                        fprintf(stdout, "%s:%d Failed to add read rule %d : rc = %d\n",
                                __FUNCTION__, __LINE__, i, rc);
                        goto error;
                }   
        }   
        rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 1,
                SCMP_A2(SCMP_CMP_LE, 64));
        if (rc < 0) {
                fprintf(stdout, "%s:%d Failed to add read allow rule : rc = %d\n",
                        __FUNCTION__, __LINE__, rc);
                goto error;
        } 

Y aquí está el PFC que generó:

  # filter for syscall "read" (0) [priority: 65525]
  if ($syscall == 0)
    if ($a2.hi32 >= 0)
      if ($a2.lo32 > 64)
      else
        action ALLOW;
      if ($a2.lo32 > 16384)
        action ERRNO(12);
      if ($a2.lo32 > 8192)
        action ERRNO(11);
      if ($a2.lo32 > 4096)
        action ERRNO(10);
      if ($a2.lo32 > 2048)
        action ERRNO(9);
      if ($a2.lo32 > 1024)
        action ERRNO(8);
      if ($a2.lo32 > 512)
        action ERRNO(7);
      if ($a2.lo32 > 256)
        action ERRNO(6);
      if ($a2.lo32 > 128)
        action ERRNO(5);
    else
      action ALLOW;
  # default action
  action ERRNO(34);
bug priorithigh

Todos 18 comentarios

Por cierto, haré lo que pueda para ayudar a enraizar la causa.

Usando scmp_bpf_disasm, la última libseccomp está poniendo los saltos en el orden incorrecto

CABEZA

 0014: 0x25 0x11 0x00 0x00000080   jgt 128  true:0032 false:0015
 0015: 0x25 0x0f 0x00 0x00000100   jgt 256  true:0031 false:0016
 0016: 0x25 0x0d 0x00 0x00000200   jgt 512  true:0030 false:0017
 0017: 0x25 0x0b 0x00 0x00000400   jgt 1024 true:0029 false:0018
 0018: 0x25 0x09 0x00 0x00000800   jgt 2048 true:0028 false:0019
 0019: 0x25 0x07 0x00 0x00001000   jgt 4096 true:0027 false:0020
 0020: 0x25 0x05 0x00 0x00002000   jgt 8192 true:0026 false:0021
 0021: 0x25 0x03 0x00 0x00004000   jgt 16384 true:0025 false:0022
 0022: 0x25 0x01 0x00 0x00000040   jgt 64   true:0024 false:0023
 0023: 0x06 0x00 0x00 0x7fff0000   ret ALLOW

pre-retrabajo

 0014: 0x25 0x01 0x00 0x00000040   jgt 64   true:0016 false:0015
 0015: 0x06 0x00 0x00 0x7fff0000   ret ALLOW
 0016: 0x25 0x0f 0x00 0x00004000   jgt 16384 true:0032 false:0017
 0017: 0x25 0x0d 0x00 0x00002000   jgt 8192 true:0031 false:0018
 0018: 0x25 0x0b 0x00 0x00001000   jgt 4096 true:0030 false:0019
 0019: 0x25 0x09 0x00 0x00000800   jgt 2048 true:0029 false:0020
 0020: 0x25 0x07 0x00 0x00000400   jgt 1024 true:0028 false:0021
 0021: 0x25 0x05 0x00 0x00000200   jgt 512  true:0027 false:0022
 0022: 0x25 0x03 0x00 0x00000100   jgt 256  true:0026 false:0023
 0023: 0x25 0x01 0x00 0x00000080   jgt 128  true:0025 false:0024
 0024: 0x06 0x00 0x00 0x00050022   ret ERRNO(34)

Interesante. Entonces, el PFC parece ser "correcto", pero el BPF generado es ... al revés. Impar. Especialmente dado que la confirmación no cambió el código de generación de BPF.

Me pregunto si los valores de prioridad se están estropeando de alguna manera.

Perdón por la ambigüedad. El PFC (publicar el cambio de retrabajo) también está en el orden incorrecto. El PFC que publiqué arriba es el orden en el que estaba antes de la reelaboración de db.c.

Aquí está el PFC generado actualmente por HEAD

  # filter for syscall "read" (0) [priority: 65525]
  if ($syscall == 0)
    if ($a2.hi32 >= 0)
      if ($a2.lo32 > 128)
        action ERRNO(5);
      if ($a2.lo32 > 256)
        action ERRNO(6);
      if ($a2.lo32 > 512)
        action ERRNO(7);
      if ($a2.lo32 > 1024)
        action ERRNO(8);
      if ($a2.lo32 > 2048)
        action ERRNO(9);
      if ($a2.lo32 > 4096)
        action ERRNO(10);
      if ($a2.lo32 > 8192)
        action ERRNO(11);
      if ($a2.lo32 > 16384)
        action ERRNO(12);
      if ($a2.lo32 > 64) 
      else
        action ALLOW;
    else
      action ALLOW;
  # default action
  action ERRNO(34);

Está bien, eso tiene un poco más de sentido. Los problemas definitivamente viven en algún lugar de la capa db.

Es un poco curioso cómo es exactamente al revés.

Encontré el problema. En la gestión de argumentos en cadena, el comportamiento de lvl_nxt y lvl_prv se intercambió después de la reelaboración masiva de db.c. Un par de pequeños cambios en _db_tree_add () hicieron que coincidiera con el comportamiento anterior de libseccomp.

Aquí hay una rama con la solución.
https://github.com/drakenclimber/libseccomp/tree/issues/112

Limpiaré los cambios, agregaré una prueba o dos y me aseguraré de que la cobertura del código esté a la altura.

Encontré algo de tiempo esta mañana, probablemente justo antes de que publicaras lo anterior, y decidí investigar un poco esto. Parece que llegamos prácticamente a la misma conclusión, aunque las correcciones son ligeramente diferentes. Aquí está mi solución actual, aunque como la suya, necesita algo de trabajo / limpieza adicional:

No estoy seguro de qué enfoque me gusta más en este momento, necesito pensar un poco en esto, ¿pensamientos?

Hmmm ... no mentiré; No estoy enamorado de ninguna solución en este momento.

El mío es simple, pero ignoró por completo _db_tree_prune () que, como dijiste en tu esencia, probablemente tenga problemas similares.

Me gusta su idea de reelaborar la macro gt () para utilizar las macros lt () y eq (), pero se están volviendo difíciles de manejar, especialmente lt (). ¿Hay alguna razón para no convertir lt () en una función en línea?
EDITAR - Acabo de notar que hiciste un comentario similar en la esencia.

Ejecuté gdb contra los antiguos libseccomp y HEAD, y los comportamientos de lvl_prv y lvl_nxt cambiaron, pero quizás eso no sea un gran problema ya que es una variable interna que nadie debería ver excepto nosotros.

Supongo que después de tanto divagar ... no lo sé. Estoy de acuerdo, necesito pensarlo;)

Hmmm ... no mentiré; No estoy enamorado de ninguna solución en este momento.

Me preocupa que haya algunos errores sutiles al reordenar un nivel de árbol como este, aunque parece que quizás el nivel fue reordenado por esa confirmación anterior y este es uno de los errores sutiles.

De cualquier manera, quiero entender cuál debería ser el orden deseado para un nivel: ¿"más grande" primero o "más grande" al final? Una vez que entendamos eso, podemos seguir adelante con las pruebas / reparaciones. Creo que la respuesta, aunque no sea por otra razón que la compatibilidad con versiones 2.x anteriores, es "más grande" primero, pero no puedo decirlo con certeza en este momento.

El mío es simple, pero ignoró por completo _db_tree_prune () que, como dijiste en tu esencia, probablemente tenga problemas similares.

Ambos básicamente hacen lo mismo en principio, el mío va un poco más allá al agregar algunas condiciones adicionales y limpiar la macro db_chain_lt (x, y).

Me gusta su idea de reelaborar la macro gt () para utilizar las macros lt () y eq (), pero se están volviendo difíciles de manejar, especialmente lt (). ¿Hay alguna razón para no convertir lt () en una función en línea?

Principalmente razones históricas. Comenzaron su vida como macros mucho más simples, pero han crecido bastante hasta el punto en que creo que probablemente deberían ser funciones. Creo que también sería bueno evaluar si realmente necesitan estar en el archivo de encabezado, creo que solo los usa src / db.c.

Ejecuté gdb contra los antiguos libseccomp y HEAD, y los comportamientos de lvl_prv y lvl_nxt cambiaron, pero quizás eso no sea un gran problema ya que es una variable interna que nadie debería ver excepto nosotros.

Sí, es un estado / árbol interno, no estoy demasiado preocupado por eso. Lo importante es la corrección del filtro generado.

Supongo que después de tanto divagar ... no lo sé. Estoy de acuerdo, necesito pensarlo;)

Je. Demos a esto uno o dos días y reagruparnos :) En este momento, esto no afecta a ninguna versión lanzada, solo está en la rama maestra, así que tenemos algo de tiempo para hacer las cosas bien.

En este momento, esto no afecta a ninguna versión lanzada, solo está en la rama maestra, por lo que tenemos algo de tiempo para hacer las cosas bien.

Suena bien. Haré algunas pruebas mientras contemplamos un plan

Escribí un programa para evaluar el manejo actual de seccomp A2. Todo el programa está disponible aquí:

https://gist.github.com/drakenclimber/3c6b45ecd973ee495281ef225fa5e54a

En pocas palabras, las reglas mayores que se generan en un orden de "última creación" "primer procesamiento".

  • Para un filtro donde las reglas > se crean en orden ascendente , p. Ej.
    seccomp_rule_add(ctx, action1, syscall, 1, SCMP(SCMP_CMP_GT, 10)
    seccomp_rule_add(ctx, action2, syscall, 1, SCMP(SCMP_CMP_GT, 20)
    seccomp_rule_add(ctx, action3, syscall, 1, SCMP(SCMP_CMP_GT. 30)
    entonces el filtro se comportará de manera coherente, p. ej.
if (A2 > 30)
    do action3
if (A2 > 20)
    do action2
if (A2 > 10)
    do action1
  • Para un filtro donde las reglas > se crean en orden descendente , p. Ej.
    seccomp_rule_add(ctx, action3, syscall, 1, SCMP(SCMP_CMP_GT, 30)
    seccomp_rule_add(ctx, action2, syscall, 1, SCMP(SCMP_CMP_GT, 20)
    seccomp_rule_add(ctx, action1, syscall, 1, SCMP(SCMP_CMP_GT. 10)
    entonces se creará el filtro, pero se comportará de manera extraña. Se producirá un código muerto. Las dos últimas declaraciones if no se pueden alcanzar
if (A2 > 10)
    do action1
if (A2 > 20)
    do action2
if (A2 > 30)
    do action1
  • Actualmente, seccomp no permite filtros con múltiples operaciones < A2. Esto parece extraño porque no pude encontrar una manera de hacer el <= equivalente al filtro > anterior
tom<strong i="43">@OracleDesktop</strong> $ ./a2test 3
Failed to add rule
        action = 0x5000e op = 0x3 datum = 18000 rc = -17
Mode 3 (LE descending) test failed.  rc = -17
tom<strong i="46">@OracleDesktop</strong> $ ./a2test 4
Failed to add rule
        action = 0x50006 op = 0x3 datum = 250 rc = -17
Mode 4 (LE ascending) test failed.  rc = -17

Supongo que la lógica else if enterrada profundamente en src/db.c está causando las fallas < , por ejemplo. No estoy seguro de si vale la pena cambiarlo / arreglarlo.

Intentaré convertir parte de este código en pruebas automatizadas para que podamos capturar el comportamiento actual.

Como está escrito, la esencia aquí falló las pruebas automatizadas que agregué la semana pasada. Profundizaré y trataré de averiguar por qué.

 batch name: 43-sim-a2_order
 test mode:  c
 test type:  bpf-sim
Test 43-sim-a2_order%%001-00001 result:   SUCCESS
Test 43-sim-a2_order%%002-00001 result:   SUCCESS
Test 43-sim-a2_order%%003-00001 result:   SUCCESS
Test 43-sim-a2_order%%004-00001 result:   SUCCESS
Test 43-sim-a2_order%%005-00001 result:   SUCCESS
Test 43-sim-a2_order%%006-00001 result:   SUCCESS
Test 43-sim-a2_order%%007-00001 result:   SUCCESS
Test 43-sim-a2_order%%008-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%009-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%010-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%011-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%012-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%013-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%014-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%015-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%016-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%017-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%018-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%019-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)
Test 43-sim-a2_order%%020-00001 result:   FAILURE bpf_sim resulted in ERRNO(5)

Mi mal - apliqué mal la esencia. Las pruebas están pasando. Uf :)

¡Decir ah! :)

Pensé que hice las pruebas en su contra, pero estaba jugando con muchas cosas en ese momento, así que pensé que estaba recordando mal. Gracias por seguir mirando esto, todavía estoy un poco atascado con SELinux y audit, pero dado que el kernel está en -rc5 en este momento, espero que se calme tan pronto como coloque las interrupciones en el nuevo código antes de la fusión ventana ...

No hay problema. Eso es definitivamente una prioridad más alta.

He estado analizando su esencia a través de varias pruebas poco realistas. No he conseguido que se rompa, pero hasta ahora solo estoy ejercitando partes de _db_tree_prune (). Estoy empezando a sentirme más cómodo con los cambios, pero quiero dedicarle un poco más de tiempo.

Toqué y pinché en el código _db_tree_prune () y no pude romperlo. Test 08-sim-subtree_checks realmente hace un buen trabajo probando la mayoría de las rutas de código dentro de prune ().

Creo que los cambios de tu esencia están listos.

Envié la solicitud de extracción n. ° 115. Creo que esto está listo para rodar

Cerrando, ya que esto debería resolverse ahora (ver historial arriba).

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