-
Notifications
You must be signed in to change notification settings - Fork 17.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: runtime
: add LockOSThreadRecursive
to lock descendent goroutines recursively (for fine-grained Linux Landlock)
#70993
Comments
Related Issues
Related Code Changes (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
Given that this was previously rejected due to implementation complexity on the Go side, I don't see how that has changed significantly enough to warrant a reevaluation. |
The previous discussion in 2018 predates the introduction of asynchronous preemption ( |
I don't understand how this could work in practice. If every goroutine is locked to the caller's thread, even the simplest channel communication seems likely to deadlock. Note that asynchronous preemption did not affect the scheduler in a significant way. The scheduler already supported synchronous preemption, asynchronous preemption just added another preemption path. |
Hi! I am a Landlock contributor in the Linux kernel and maintainer of the Go-Landlock library. I also do not believe that this can be made to work - A Landlock sandbox that only applies to a subset of a processes' threads would just be as strong as the boundaries that the runtime can enforce between these (OS) threads, to keep nefarious threads to influence other threads' behavior. However, threads influencing each others behavior or scheduling work for each other is a normal thing that (intentionally) happens in multi-threaded programs and there are no strong security boundaries between them that would keep them from messing with each other's data (e.g. through object aliasing). Enforcing a Landlock policy on only one thread would make the entire code in userspace (Go runtime, all loaded program code) the "Trusted Computing Base" for that Landlock sandbox, and it seems unrealistic to harden that in a way so that threads can't influence each other. TL;DR:
Also see my remark on the original bug: landlock-lsm/go-landlock#32 (comment) P.S.: The same reasoning applies to other programming languages as well, which is why "enforcing Landlock for the full process" is one of the first bugs we filed (landlock-lsm/linux#2). The current per-thread restriction is an unfortunate implementation artifact due to the way how the kernel "credential" swizzling is implemented, similar to the way setuid(2) is implemented under the hood. |
Proposal Details
Currently,
runtime.LockOSThread
does not lock the caller's descendent goroutines to the caller's OS thread recursively.I'd like to propose adding a new function
LockOSThreadRecursive
to allow recursive locking.This proposal does not affect the behavior of the existing
LockOSThread
function.Expected usecase: Landlock
One of the expected usecases is to apply Landlock (Linux kernel's security module) to a specific package/function of Go program while leaving other packages/functions unconfined.
The target package/function will be wrapped with a goroutine that is recursively locked to a specific OS thread.
https://docs.kernel.org/userspace-api/landlock.html
LockOSThreadRecursive
will ensure that the Landlock policy isn't accidentally jailbroken when the goroutine spawns child goroutines.(Side note: the meaning of "lock" in "Landlock" is different from "lock" in "LockOSThread")
Caveats
A thread confined with Landlock may still affect other threads' behavior by changing other threads' variables, via
unsafe
,cgo
,asm
,reflect
,/proc/PID/mem
,process_vm_writev(2)
, etc.These loopholes may be detected with a third-party linter tool.
Relevant discussion
There was once a similar proposal submitted by another person on February 9th, 2018:
Their proposal was rejected on March 6th, 2018: #23758 (comment)
However, as the context has changed since 2018 (Landlock wasn't available in the upstream Linux kernel at that time), it might be worth reconsidering the decision.
Implementation
As discussed in 2018, the implementation will not be straightforward.
src/runtime/runtime2.go
:struct m
:lockedg guintptr
has to be changed to a slicesrc/runtime/proc.go
:schedule
,startlockedm
, etc. have to be changed to force preemption of multipleg
on a singlem
The text was updated successfully, but these errors were encountered: