go x/sys/unix: Capget/Capset 写入超过结构体内存范围

x9ybnkn6  于 5个月前  发布在  Go
关注(0)|答案(8)|浏览(78)

我有一个使用Unix的go程序,它在使用Capget/Capset时随机崩溃。
感谢@ianlancetaylor,这段代码被识别为罪魁祸首:

hdr := unix.CapUserHeader{Version: unix.LINUX_CAPABILITY_VERSION_3}
var data unix.CapUserData
unix.Capget(&hdr, &data) // this might write past &data.

事实证明,版本2和版本3的Linux功能比版本1的功能更宽。
版本1使用32位字段,完美地适应单个 unix.CapUserData :

type CapUserData struct {
	Effective   uint32
	Permitted   uint32
	Inheritable uint32
}

然而,版本2和版本3的功能是64位,无法适应单个 unix.CapUserData
尽管如此,Linux的capget系统调用使用了相同的结构体,但将其写入两个示例的结构体中。它将功能的低位写入第一个结构体,将高位写入第二个结构体。
因此,令人困惑的是,上述代码的正确编写方式应该是:

hdr := unix.CapUserHeader{Version: unix.LINUX_CAPABILITY_VERSION_3}
var data [2]unix.CapUserData
unix.Capget(&hdr, &data[0])

这是C语言中的正常做法,但对我来说看起来不像典型的Go。
这段代码将低位写入 data[0] ,高位写入 data[1] 。然后客户端在设置功能时必须小心,根据功能选择 data[0]data[1] :

data[0].Effective |= 1<<unix.CAP_SYS_ADMIN
data[1].Effective |= 1<<(unix.CAP_CHECKPOINT_RESTORE - 32)

对于unix.Capset也是如此,只是它会读取第一个结构体之外的内容。
我在想,unix.Capget/Capset的签名是否应该更改为类似这样:

func Capget(hdr *CapUserHeader, data *[2]CapUserData) (err error)

以防止类似这样的问题。这样的签名将清楚地表明syscall影响了多少内存。
或者也许在调用过程中扩大字段宽度并重新打包以应对奇怪的内存布局?但这在Linux继续扩展功能并变得更宽的情况下可能不会很好地工作。
大家有什么想法吗?

kfgdxczn

kfgdxczn1#

你是否考虑过使用 cap 包替代?

gmxoilav

gmxoilav2#

嘿,Andrew,谢谢你的建议!
那个看起来像是一个很棒的库,然而在阅读了一会儿后,我意识到它会修改整个过程的能力。我以为它也会让我改变单个操作系统线程的能力,但看起来似乎不可能(或者我可能遗漏了一些东西)。
在我的使用场景中,我正在做以下操作:

go func() { 
  runtime.LockOSThread() // exclusively bind the goroutine to a single os thread.
  unix.Capget/Capset
  unix.Setns // change netns of the os thread
  exec.Start/Wait/Run/etc
  // exit goroutine without unlocking to kill the os thread
} ()
tuwxkamq

tuwxkamq4#

感谢指正!
这对我来说不起作用,因为我需要先提升权限,然后再设置命名空间。回调似乎在capset之前就被调用了(对于丢弃权限的使用场景来说,这是完全有道理的)。此外,我希望使用标准的exec包在网络命名空间中运行程序(以标准方式处理stdin/stdout)。
是否有计划在cap.singlesc syscaller下公开cap API?或者,有没有类似于Launch的东西,但只有回调。这样,我就可以在回调中使用exec.Start/Wait等,而不是依赖于cap包中的ForkExec。
谢谢!

6kkfgxo0

6kkfgxo05#

你应该自由地实现这样一个特性 requestcap.Launch() 和回调机制都非常开放以供开发。
[尽管如此,关于POSIX语义在一般权限机制中的重要性似乎存在很多困惑。我 wrote this to capture how trivial it is for unprivileged threads to trick the other threads into doing privileged things ,这适用于C/C++,但也适用于Go/CGo。]

kxxlusnw

kxxlusnw6#

此外,我忘记提到了,Launch() 的内容会使用当前进程的能力调用回调函数,所以如果你的回调函数需要在pE中提升某些东西,你可以在调用Launch()之前提升它们。我很乐意解决你发现的任何问题。

jutyujz0

jutyujz07#

你应该自由地提出这样的功能请求
谢谢!完成:https://bugzilla.kernel.org/show_bug.cgi?id=211919
你可以在调用Launch()之前提出这些要求
但那时我又回到了使用裸syscalls而不是漂亮的libcap API的原点。

pobjuy32

pobjuy328#

我认为你需要在x/sys/unix上发起一个PR,为这些函数添加文档(以及/或者使用示例)。

相关问题