Libseccomp: BUG:A2 处理被 src/db.c 返工破坏

创建于 2018-02-26  ·  18评论  ·  资料来源: seccomp/libseccomp

为了测试我提议的二叉树性能改进,我为 read() 及其缓冲区大小参数 (A2) 编写了一组不切实际的规则。 但似乎 src/db.c 返工提交(ce3dda9a1)破坏了 A2 处理 - 至少对于这个测试用例。

在数据库返工提交之前,读取如下 - read(devzero_fd, buf, 8000) - 返回-10 。 在这次提交之后,它现在返回-5

这是我用来生成愚蠢的 read() 规则的 C 代码:

        /* 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;
        } 

这是它生成的 PFC:

  # 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

所有18条评论

顺便说一句,我会尽我所能帮助根本原因

使用 scmp_bpf_disasm,最新的 libseccomp 将跳转的顺序不正确

 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

返工前

 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)

有趣的。 所以 PFC 看起来是“正确的”,但生成的 BPF 是......倒退。 奇怪的。 特别是考虑到提交没有改变 BPF 生成代码。

我想知道优先级值是否以某种方式搞砸了?

很抱歉有歧义。 PFC(返工更改后)的顺序也不正确。 我在上面发布的 PFC 是 db.c 返工之前的顺序。

这是 HEAD 当前生成的 PFC

  # 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);

好吧,这更有意义一些。 问题肯定存在于 db 层的某个地方。

它是如何完全倒退的,这有点有趣。

我发现了这个问题。 在链参数管理中,lvl_nxt 和 lvl_prv 的行为在大规模 db.c 返工后交换。 对 _db_tree_add() 的一些小改动使其与之前的 libseccomp 行为相匹配。

这是一个带有修复程序的分支
https://github.com/drakenclimber/libseccomp/tree/issues/112

我将清理更改,添加一两个测试,并确保代码覆盖率达到要求。

我今天早上找到了一些时间,可能就您发布上述内容

我不确定我现在更喜欢哪种方法,我需要考虑一下,想法?

嗯……我不会说谎; 在这一点上,我对这两种修复都不着迷。

我的很简单,但它完全忽略了 _db_tree_prune() - 就像你在你的要点中所说的 - 可能有类似的问题。

我喜欢你重新设计 gt() 宏以利用 lt() 和 eq() 宏的想法,但它们变得笨拙了——尤其是 lt()。 是否有任何理由不将 lt() 转换为内联函数?
编辑 - 我刚刚注意到你在要点中发表了类似的评论。

我针对旧的 libseccomp 和 HEAD 运行了 gdb,并且 lvl_prv 和 lvl_nxt 的行为确实发生了变化,但这也许没什么大不了的,因为它是一个内部变量,除了我们之外,没有人应该看到。

我想毕竟这些漫无边际的...我不知道。 我同意,我需要考虑一下;)

嗯……我不会说谎; 在这一点上,我对这两种修复都不着迷。

我担心像这样重新排序树级别会存在一些微妙的错误,尽管看起来该级别可能先前的提交重新排序,是一个微妙的错误。

无论哪种方式,我都想了解一个级别所需的排序应该是什么:首先是“最大”,还是最后是“最大”? 一旦我们理解了这一点,我们就可以继续进行测试/修复。 我认为答案,如果除了与以前的 2.x 版本兼容之外没有其他原因,首先是“最大的”,但此时我不能肯定地说。

我的很简单,但它完全忽略了 _db_tree_prune() - 就像你在你的要点中所说的 - 可能有类似的问题。

它们在原则上基本上都做同样的事情,我的通过添加一些额外的条件和清理 db_chain_lt(x,y) 宏更进一步。

我喜欢你重新设计 gt() 宏以利用 lt() 和 eq() 宏的想法,但它们变得笨拙了——尤其是 lt()。 是否有任何理由不将 lt() 转换为内联函数?

主要是历史原因。 它们开始时是更简单的宏,但它们已经发展到我认为它们可能应该是函数的地步。 我认为评估它们是否真的需要在头文件中也很好,我相信它们只被 src/db.c 使用。

我针对旧的 libseccomp 和 HEAD 运行了 gdb,并且 lvl_prv 和 lvl_nxt 的行为确实发生了变化,但这也许没什么大不了的,因为它是一个内部变量,除了我们之外,没有人应该看到。

是的,这是一个内部状态/树,我不太担心。 重要的是生成的过滤器的正确性。

我想毕竟这些漫无边际的...我不知道。 我同意,我需要考虑一下;)

呵呵。 让我们花一两天时间重新组合 :) 现在这不会影响任何已发布的版本,它只在主分支中,所以我们有一些时间来解决问题。

现在这不会影响任何已发布的版本,它只在主分支中,所以我们有一些时间来解决问题。

听起来不错。 我会做一些测试,同时我们考虑一个计划

我编写了一个程序来评估当前的 seccomp A2 处理。 整个程序可在此处获得:

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

简而言之,大于规则是以“最后创建”“第一次处理”的顺序生成的。

  • 对于>规则按升序创建的过滤器,例如
    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)
    那么过滤器将以连贯的方式运行,例如
if (A2 > 30)
    do action3
if (A2 > 20)
    do action2
if (A2 > 10)
    do action1
  • 对于>规则按降序创建的过滤器,例如
    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)
    那么过滤器将被创建,但行为很奇怪。 将产生死代码。 最后两个if语句不可达
if (A2 > 10)
    do action1
if (A2 > 20)
    do action2
if (A2 > 30)
    do action1
  • seccomp 当前不允许具有多个< A2 操作的过滤器。 这看起来很奇怪,因为我无法找到一种方法来使<=相当于上面的>过滤器
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

我猜深埋src/db.celse if逻辑导致<失败,例如 . 我不确定是否值得更改/修复。

我将尝试将其中一些代码转换为自动化测试,以便我们可以捕获当前行为。

正如所写,这里的要点未能通过我上周添加的自动化测试。 我会深入研究并尝试找出原因。

 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)

我的不好 - 我误用了要点。 测试正在通过。 呼:)

哈! :)

以为我对它进行了测试,但当时我玩了很多东西,所以我想我只是记错了。 感谢您继续关注这个问题,我仍然对 SELinux 和审计感到有些困惑,但是由于内核现在处于 -rc5,我希望在合并之前对新代码进行中断后它会很快平静下来窗户 ...

不用担心。 这绝对是更高的优先级。

我一直在通过各种不切实际的测试来运行您的要点。 我还没有让它坏掉,但到目前为止我也只是在练习 _db_tree_prune() 的点点滴滴。 我开始对这些变化感到更舒服,但我想多花点时间。

我戳了戳_db_tree_prune() 代码,但我无法破解它。 测试 08-sim-subtree_checks 在测试 prune() 中的大部分代码路径方面确实做得很好。

我认为你的要点的变化很好。

我提交了拉取请求 #115。 我想这已经准备好了

关闭,因为现在应该解决这个问题(见上面的历史)。

此页面是否有帮助?
0 / 5 - 0 等级