Libseccomp: BOGUE : A2 Manipulation interrompue par src/db.c Rework

Créé le 26 févr. 2018  ·  18Commentaires  ·  Source: seccomp/libseccomp

Pour tester mes propositions d'amélioration des performances de l'arbre binaire, j'ai écrit un ensemble de règles irréalistes pour read() et son argument de taille de tampon (A2). Mais il semble que le commit de retravail src/db.c (ce3dda9a1) ait cassé le traitement A2 - au moins pour ce cas de test.

Avant le commit de remaniement de la base de données, une lecture comme celle-ci - read(devzero_fd, buf, 8000) - a renvoyé -10 . Après ce commit, il retourne maintenant -5 .

Voici le code C que j'ai utilisé pour générer mes règles stupides 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;
        } 

Et voici le PFC qu'il a généré :

  # 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

Tous les 18 commentaires

Au fait, je ferai ce que je peux pour aider à la cause

En utilisant scmp_bpf_disasm, la dernière libseccomp met les sauts dans le mauvais ordre

DIRIGER

 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

pré-retravailler

 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)

Intéressant. Le PFC semble donc être "correct", mais le BPF généré est... à l'envers. Impair. Surtout étant donné que le commit n'a pas changé le code de génération de BPF.

Je me demande si les valeurs de priorité sont en train de se tromper d'une manière ou d'une autre ?

Désolé pour l'ambiguïté. Le PFC (après le changement de retouche) est également dans le mauvais ordre. Le PFC que j'ai posté ci-dessus est l'ordre dans lequel il se trouvait avant la refonte de db.c.

Voici le PFC actuellement généré par 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);

Bon, ça a un peu plus de sens. Les problèmes vivent définitivement quelque part dans la couche db.

C'est un peu drôle comme c'est exactement à l' envers.

J'ai trouvé le problème. Dans la gestion des arguments de la chaîne, le comportement de lvl_nxt et lvl_prv a été échangé après la refonte massive de db.c. Quelques petites modifications apportées à _db_tree_add() l'ont fait correspondre au comportement précédent de libseccomp.

Voici une branche avec le correctif
https://github.com/drakenclimber/libseccomp/tree/issues/112

Je vais nettoyer les modifications, ajouter un test ou deux et m'assurer que la couverture du code est à la hauteur.

J'ai trouvé du temps ce matin, probablement juste avant que vous ne publiiez ce qui précède, et j'ai décidé d'examiner un peu la question. Il semble que nous soyons arrivés à peu près à la même conclusion, bien que les correctifs soient légèrement différents. Voici mon correctif actuel, bien que, comme le vôtre, il nécessite un travail/un nettoyage supplémentaire :

Je ne sais pas quelle approche j'aime le plus en ce moment, j'ai besoin d'y réfléchir un peu, des pensées ?

Hmmm... Je ne mentirai pas ; Je ne suis pas amoureux de l'une ou l'autre solution à ce stade.

Le mien est simple, mais il ignore complètement _db_tree_prune() qui - comme vous l'avez dit dans votre résumé - a probablement des problèmes similaires.

J'aime votre idée de retravailler la macro gt() pour utiliser les macros lt() et eq(), mais elles deviennent lourdes - en particulier lt(). Y a-t-il une raison de ne pas convertir lt() en fonction en ligne ?
EDIT - Je viens de remarquer que vous avez fait un commentaire similaire dans l'essentiel.

J'ai exécuté gdb contre l'ancienne libseccomp et HEAD, et les comportements de lvl_prv et lvl_nxt ont changé, mais ce n'est peut-être pas grave car c'est une variable interne que personne d'autre que nous ne devrait voir.

Je suppose qu'après toutes ces divagations... Je ne sais pas. Je suis d'accord, il faut que j'y réfléchisse ;)

Hmmm... Je ne mentirai pas ; Je ne suis pas amoureux de l'une ou l'autre solution à ce stade.

Je suis inquiet qu'il ya quelques bugs subtils avec un niveau réordonnancement d'arbre comme celui - ci, bien qu'il ne semble peut - être comme le niveau a été réorganisés par commit précédent et c'est l' un des bugs subtils.

Quoi qu'il en soit, je veux comprendre quel devrait être l'ordre souhaité pour un niveau : "le plus grand" en premier, ou "le plus grand" en dernier ? Une fois que nous comprenons cela, nous pouvons aller de l'avant avec les tests/réparations. Je pense que la réponse, ne serait-ce que pour la compatibilité avec les versions 2.x précédentes, est "la plus grande" en premier, mais je ne peux pas l'affirmer avec certitude à ce stade.

Le mien est simple, mais il ignore complètement _db_tree_prune() qui - comme vous l'avez dit dans votre résumé - a probablement des problèmes similaires.

Ils font tous les deux fondamentalement la même chose en principe, le mien va un peu plus loin en ajoutant quelques conditions supplémentaires et en nettoyant la macro db_chain_lt(x,y).

J'aime votre idée de retravailler la macro gt() pour utiliser les macros lt() et eq(), mais elles deviennent lourdes - en particulier lt(). Y a-t-il une raison de ne pas convertir lt() en fonction en ligne ?

Principalement des raisons historiques. Au départ, elles étaient des macros beaucoup plus simples, mais elles se sont beaucoup développées au point où je pense qu'elles devraient probablement être des fonctions. Je pense qu'il serait également bon d'évaluer s'ils ont vraiment besoin d'être dans le fichier d'en-tête, je pense qu'ils ne sont utilisés que par src/db.c.

J'ai exécuté gdb contre l'ancienne libseccomp et HEAD, et les comportements de lvl_prv et lvl_nxt ont changé, mais ce n'est peut-être pas grave car c'est une variable interne que personne d'autre que nous ne devrait voir.

Ouais, c'est un état/arbre interne, je ne suis pas trop inquiet à ce sujet. L'important est l'exactitude du filtre généré.

Je suppose qu'après toutes ces divagations... Je ne sais pas. Je suis d'accord, il faut que j'y réfléchisse ;)

Il h. Laissons cela un jour ou deux et regroupons-nous :) Pour le moment, cela n'affecte aucune version publiée, c'est uniquement dans la branche master, nous avons donc un peu de temps pour bien faire les choses.

Pour le moment, cela n'affecte aucune version publiée, c'est uniquement dans la branche master, nous avons donc un peu de temps pour faire les choses correctement.

Ça a l'air bien. Je vais faire quelques tests pendant que nous réfléchissons à un plan

J'ai écrit un programme pour évaluer la gestion actuelle de seccomp A2. Tout le programme est disponible ici :

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

En un mot, les règles supérieures à sont générées dans un ordre « dernière création », « premier traité ».

  • Pour un filtre où les règles > sont créées dans un ordre croissant , par exemple
    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)
    alors le filtre se comportera de manière cohérente, par exemple
if (A2 > 30)
    do action3
if (A2 > 20)
    do action2
if (A2 > 10)
    do action1
  • Pour un filtre où les règles > sont créées dans un ordre décroissant , par exemple
    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)
    alors le filtre sera créé, mais se comportera bizarrement. Un code mort sera produit. Les deux dernières instructions if sont inaccessibles
if (A2 > 10)
    do action1
if (A2 > 20)
    do action2
if (A2 > 30)
    do action1
  • Les filtres avec plusieurs opérations < A2 ne sont actuellement pas autorisés par seccomp. Cela semble étrange parce que je ne pouvais pas trouver un moyen de faire le <= équivalent de > filtre au- dessus
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

Je suppose que la logique else if enfouie profondément dans src/db.c est à l'origine des échecs de < , par exemple . Je ne sais pas si cela vaut la peine de changer/réparer.

Je vais essayer de convertir une partie de ce code en tests automatisés afin que nous puissions capturer le comportement actuel.

Comme écrit, l'essentiel ici a échoué aux tests automatisés que j'ai ajoutés la semaine dernière. Je vais creuser et essayer de comprendre pourquoi.

 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)

Mon mauvais - j'ai mal appliqué l'essentiel. Les tests passent. Phew :)

Ha! :)

Je pensais avoir fait les tests contre lui, mais je jouais avec beaucoup de choses à ce moment-là, alors j'ai pensé que je me souvenais juste de mal. Merci de continuer à regarder cela, je suis toujours un peu enlisé avec SELinux et l'audit, mais comme le noyau est à -rc5 en ce moment, je m'attends à ce qu'il se calme dès que je mets les pauses sur le nouveau code avant la fusion la fenêtre ...

Pas de soucis. C'est certainement une priorité plus élevée.

J'ai passé en revue votre essence à travers divers tests irréalistes. Je n'ai pas réussi à le casser, mais je n'exerce également que des morceaux de _db_tree_prune() jusqu'à présent. Je commence à me sentir plus à l'aise avec les changements, mais je veux y consacrer un peu plus de temps.

J'ai poussé et poussé le code _db_tree_prune() et je ne pouvais pas le casser. Test 08-sim-subtree_checks fait vraiment du bon travail pour tester la plupart des chemins de code dans prune().

Je pense que les changements de votre essence sont bons pour aller.

J'ai soumis une pull request #115. Je pense que c'est prêt à rouler

Fermeture car cela devrait maintenant être résolu (voir l'historique ci-dessus).

Cette page vous a été utile?
0 / 5 - 0 notes