jsipprell / keyctl

A Go-lang interface to the linux kernel keyring api

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

keyctl function appears unsafe with respect to garbage collection

JohnRusk opened this issue · comments

( @jsipprell , I think I've understood the following correctly - but I'd be delighted to be proved wrong :-) )

Syscalls in Go require that we follow a rule in which the final cast of parameters, from unsafe.Pointer to uintptr, must happen inside the syscall. Otherwise, the garbage collector may move the pointer after we create the uintptr, but before we use it - leaving the uintptr still pointing to the old location where the object used to be.

So for instance sys_linux.add_key is fine, because although pptr is prepared earlier, it is not cast to uintprt until inside the argument list of the syscall function. So far, so good.

But.. the "keyctl" wrapper function is not doing the same thing. It relies on its callers to do the cast to uintptr. Therefore that cast does not happen inside the arg list of the syscall, and it's unsafe.

func keyctl(cmd keyctlCommand, args ...uintptr) (r1 int32, r2 int32, err error) {

This could, in theory, result in various types of undefined behavior when keyctl is used in an app with any non-trivial amount of garbage collector activity. I have not observed any such behavior in practice. (I just noticed the code structure while trying to debug something else).

Two corrections:
(a) The current issue relates to stack copying, not CC. (I have edited the title of this issue accordingly)
(b) The possible role of GC is just that one day, Go might introduce a copying collector.

But, stack copying is still a current issue. If something happens that causes a goroutine to run out of stack, then all of its existing stack is copied, and pointers within that stack will change. In the case of the keyctl method, it allocates a small array on its first line. If that array just happens to be the thing that exhausts the goroutines current stack space, then the problem will be triggered. It could also (I think) be triggered if the call to keyctl itself (pushing call-related stuff to the stack) triggers the move). The only other thing I can think of which might trigger the issue is if debugSysCalls is true, and the log call pushes the stack over its limit.

cc @jiacfan

BTW, more references on moving related to stacks here: https://stackoverflow.com/a/22209698/

and the following general guidance here:

Pointers should have been kept in unsafe.Pointers - not uintptrs - always.
and
... conversion to uintptr and back. The answer today is that you have to do it "quickly". Within a single statement is certainly fine and likely to remain so. Splitting the work across multiple statements but no function calls is probably fine but may not remain so. Splitting the work across function calls is not fine and likely to break very soon

I imagine the latter points about splitting also apply to uintptrs to be used for syscalls, but that's not mentioned because the author has already pointed out that the cast to unitptr should happen inside the syscall's arg list.

Thanks for the report. This should be fixed in master.