Libseccomp: Q: 在不同的线程中不能有不同的过滤器和 SCMP_ACT_NOTIFY

创建于 2020-11-06  ·  9评论  ·  资料来源: seccomp/libseccomp

libseccomp 将通知 fd 存储在全局变量中: state.notify_fd 。 这使得无法将 libseccomp 用于在不同线程中具有不同过滤器的多线程应用程序(即不使用 TSYNC)。

libseccomp 有seccomp_reset(NULL, ...)来重置全局变量state.notify_fd 。 但是seccomp_reset()具有重置state.nr_seccomp = -1的不幸后果。

尝试解决 libseccomp-golang 中的单元测试时注意到此问题,请参阅https://github.com/seccomp/libseccomp-golang/pull/59#issuecomment -723045033

抄送@yvesf @rata

prioritlow question

最有用的评论

听起来我们都同意了,所以我要结束这个问题。 @alban如果你认为我们遗漏了一些重要的东西,请告诉我们和/或重新打开这个问题,以便我们可以解决所有问题。

感谢大家。

所有9条评论

@drakenclimber保留即将发布的 v2.5.1 版本,直到我们对此进行澄清。 我不确定我是否完全理解原始报告,但由于 v2.5.1 是第一个实现seccomp_reset(NULL, ...)概念的版本,让我们稍等片刻,直到我们可以验证......

将其添加到 v2.5.1 里程碑只是作为目前的阻滞剂,随着我们调查事情,这可能会改变。

@drakenclimber保留即将发布的 v2.5.1 版本,直到我们对此进行澄清。 我不确定我是否完全理解原始报告,但由于 v2.5.1 是第一个实现seccomp_reset(NULL, ...)概念的版本,让我们稍等片刻,直到我们可以验证......

同意。 会做。

libseccomp 将通知 fd 存储在全局变量中: state.notify_fd 。 这使得无法将 libseccomp 用于在不同线程中具有不同过滤器的多线程应用程序(即不使用 TSYNC)。

你能详细说明最后一句话吗? 我不确定我是否理解你想表达的意思。

FWIW,seccomp 通知 FD 是一个进程全局对象,您只能从内核请求它一次。 您可以阅读https://github.com/seccomp/libseccomp/issues/273以获取有关该问题的更多背景信息。

libseccomp 有seccomp_reset(NULL, ...)来重置全局变量state.notify_fd 。 但是seccomp_reset()具有重置state.nr_seccomp = -1的不幸后果。

为什么重置state.nr_seccomp = -1是一个重大问题? 如果nr_seccomp字段在下次请求可以使用seccomp(2)的操作时重置为-1 seccomp(2) ,库会检查是否支持seccomp(2)并在可用时使用它。 是的,它可能会导致对seccomp(2)额外调用,但这不应该是一个主要问题,它是您的用例的一个问题吗?

@alban - 我的类似问题。 我花了一些时间试图发明一个合理的多线程、多 seccomp 过滤器用例(在 C 中),但真的想不出任何东西。

我通读了os.LockOSThread()的 Go 文档,这对我来说很有意义。 但我正在努力将这些知识转化为多线程、多 seccomp 过滤器解决方案。

你能分享一些伪代码或一些你在想什么的高级设计吗? 那时我很乐意用 C 对它进行原型设计。

你能详细说明最后一句话吗? 我不确定我是否理解你想表达的意思。

在 libseccomp-golang 中进行单元测试时,我意识到正在测试以下场景:

  1. 在单元测试的第一次执行中,seccomp 策略被应用而没有SECCOMP_FILTER_FLAG_TSYNC (意味着它应用于线程级别而不是进程级别)但带有SECCOMP_FILTER_FLAG_NEW_LISTENER (因此 libseccomp 将存储state.notify_fd的 fd )。
  2. 同样的单元测试在同一个进程中再次执行,但在不同的线程中(在 Go 中使用runtime.LockOSThread来确保这一点)。 但是 libseccomp 重用了来自先前 seccomp 过滤器(来自state.notify_fd )的 fd,而不是为新过滤器获取新的 fd。 然后测试失败,因为我们希望在错误的 seccomp fd 上接收事件。

FWIW,seccomp 通知 FD 是一个进程全局对象,您只能从内核请求它一次。 您可以阅读 #273 以获取有关该问题的更多背景信息。

据我了解,内核限制我们在过滤器树中只能获得一个 seccomp 通知 FD。 但是在上面的场景中,来自同一进程的两个线程使用不同的过滤器树,所以从内核的角度来看应该没问题。

这种将不同的过滤器树与SECCOMP_FILTER_FLAG_NEW_LISTENER一起使用的场景并不是故意构建的,它只是在同一进程中运行 libseccomp-golang 单元测试的结果。 但由于根本原因是 libseccomp 使用了一个在线程之间共享的全局变量state.notify_fd ,我想我应该在这里打开这个 bug 来展开讨论。 但是,如果将其关闭为“WONTFIX”(我不知道其他 libseccomp 用户是否需要支持这种情况),我会很好。 在这种情况下,我们可以以不同的方式编写 libseccomp-golang 单元测试(即为每个测试迭代使用单独的过程); 无论如何我们都必须这样做(以避免混合线程级过滤器和进程级过滤器 - 这将被内核拒绝)。

在 libseccomp-golang 中进行单元测试时,我意识到正在测试以下场景:

  1. 在单元测试的第一次执行中,seccomp 策略被应用而没有SECCOMP_FILTER_FLAG_TSYNC (意味着它应用于线程级别而不是进程级别)但带有SECCOMP_FILTER_FLAG_NEW_LISTENER (因此 libseccomp 将存储state.notify_fd的 fd )。
  2. 同样的单元测试在同一个进程中再次执行,但在不同的线程中(在 Go 中使用runtime.LockOSThread来确保这一点)。 但是 libseccomp 重用了来自先前 seccomp 过滤器(来自state.notify_fd )的 fd,而不是为新过滤器获取新的 fd。 然后测试失败,因为我们希望在错误的 seccomp fd 上接收事件。

啊哈,这现在更有意义了。 我经常想知道我们是否应该将 TSYNC 设为 libseccomp-golang 绑定的默认值; 鉴于 Go 中的线程歧义,这似乎是一个更安全的选择。

据我了解,内核限制我们在过滤器树中只能获得一个 seccomp 通知 FD。 但是在上面的场景中,来自同一进程的两个线程使用不同的过滤器树,所以从内核的角度来看应该没问题。

这种将不同的过滤器树与SECCOMP_FILTER_FLAG_NEW_LISTENER一起使用的场景并不是故意构建的,它只是在同一进程中运行 libseccomp-golang 单元测试的结果。 但由于根本原因是 libseccomp 使用了一个在线程之间共享的全局变量state.notify_fd ,我想我应该在这里打开这个 bug 来展开讨论。 但是,如果将其关闭为“WONTFIX”(我不知道其他 libseccomp 用户是否需要支持这种情况),我会很好。 在这种情况下,我们可以以不同的方式编写 libseccomp-golang 单元测试(即为每个测试迭代使用单独的过程); 无论如何我们都必须这样做(以避免混合线程级过滤器和进程级过滤器 - 这将被内核拒绝)。

我很想知道@drakenclimber 对此的看法,但是我现在的想法是,这是一个“WONTFIX”,因为它是一个相当奇怪的角落案例。 如果应用程序确实想做这样的事情,他们可以从过滤器树“A”中保存通知 fd,重置 libseccomp,然后从过滤器树“B”中检索并保存通知 fd。

为了在 libseccomp 中“修复”这个问题,我们需要让 libseccomp 线程感知,这带来了许多挑战和陷阱,因此我目前认为这将是一个坏主意。 然而,为了论证起见,如果我们确实让 libseccomp 线程感知,我们也可以使内部全局状态成为线程特定状态和/或可能是过滤器树特定状态; 在这两种情况中的任何一种情况下,我认为当前的 API(包括seccomp_reset(NULL, ...) )仍然是合理的,所以我很想保留 API 的原样。 如果有人对此有任何疑虑,请尽快告诉我们。

@drakenclimber? 除非对上述内容有任何异议,否则我认为我们将重新发布 v2.5.1。

我很想知道@drakenclimber 对此的看法,但是我现在的想法是,这是一个“WONTFIX”,因为它是一个相当奇怪的角落案例。 如果应用程序确实想做这样的事情,他们可以从过滤器树“A”中保存通知 fd,重置 libseccomp,然后从过滤器树“B”中检索并保存通知 fd。

我同意。 我花了一些时间挖掘@tych0的原始补丁集和注释以及内核代码本身。 他设想的用例是一个运行通知处理程序的监控进程,同时容器化进程来来去去。 当这些容器化进程调用带有通知的系统调用时,监控进程可以执行额外的逻辑来允许/拒绝请求。

话虽如此,我尝试提出一个具有多个通知调用程序和处理程序的多线程、单进程用例。 老实说,我无法连贯地炮制这样的场景。 由于这是一个相当人为的用例,我无法弄清楚一个现实的用例,我们应该标记这个 WONTFIX。

一方面 - 我能够让内核将多个通知 fds 返回给一个进程。 通过创建多个 pthread 并让它们立即加载一个带有通知操作的 seccomp 过滤器,偶尔我能够将两个或三个不同的通知 fds 返回给用户空间进程。 由于这太不现实了,我倾向于将此内核问题也标记为 WONTFIX。

@drakenclimber? 除非对上述内容有任何异议,否则我认为我们将重新发布 v2.5.1。

伟大的。 我应该可以在下周初开始工作。

听起来我们都同意了,所以我要结束这个问题。 @alban如果你认为我们遗漏了一些重要的东西,请告诉我们和/或重新打开这个问题,以便我们可以解决所有问题。

感谢大家。

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