-
Notifications
You must be signed in to change notification settings - Fork 17.3k
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
syscall: caching RLIMIT_NOFILE is wrong implementation #66797
Comments
Please don't show us code in images. Link to go.googlesource.com or GitHub instead. Images are difficult to read. Thanks. |
If I understand this correctly, the problem is that if process A uses We can't change the current behavior in which process B passes the original So I think the question is whether there is a way that process B could detect that the process limit was changed by some other process via |
|
I think there is also a get/set/set race situation here, please see opencontainers/runc#4266 and opencontainers/runc#4265 . Go CL 393354 (in Go 1.19beta1) introduced raising the RLIMIT_NOFILE soft limit to the current value of the hard limit. Further CLs (in Go 1.21, but backported to Go 1.20.x and 1.19.x) introduced more code in between getrlimit and setrlimit syscalls (see Go's src/syscall/rlimit.go). In runc exec, we use
So, I think we need to find a way to solve two problems:
|
What's the progress now? |
We already have a public function to clear the cache: var r syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &r)
syscall.Setrlimit(syscall.RLIMIT_NOFILE, &r) |
We have considered this method, but it will cause new get/set race. |
This only matters just before starting a child, so do it then. If that is a race, then you have a race problem no matter what. |
Maybe |
Hi @lifubang, do you agree with what Ian wrote in #66797 (comment), or if not, could you briefly expand on why? For the people reporting this here, is this something that affects Go code primarily consumed as a library, or package main? If there is not a valid workaround or solution in the near term, I wonder if a GODEBUG controlling the behavior could be temporary solution to help with backwards compatibility, or maybe that does not make sense? |
Sorry for delay, because I'm in another busy thread.
The core reason is that using Get/Set to clear the internal cache is not a atomic operation, I suggest to add a public method to clear this cache directly. For example: |
I understand the race. My point is that this only matters when you are about to start a child process. So do it then. There is already a race when starting a child process: you can start the child before or after the other process calls Two additional system calls are trivial, it's not worth adding a very special purpose operation to avoid that. |
@ianlancetaylor the problem is, this relies on Perhaps something like this will fix the issue for us: diff --git a/src/syscall/rlimit.go b/src/syscall/rlimit.go
index d77341bde9..8184f17ab6 100644
--- a/src/syscall/rlimit.go
+++ b/src/syscall/rlimit.go
@@ -39,11 +39,10 @@ func init() {
}
func Setrlimit(resource int, rlim *Rlimit) error {
- err := setrlimit(resource, rlim)
- if err == nil && resource == RLIMIT_NOFILE {
+ if resource == RLIMIT_NOFILE {
// Store nil in origRlimitNofile to tell StartProcess
// to not adjust the rlimit in the child process.
origRlimitNofile.Store(nil)
}
- return err
+ return setrlimit(resource, rlim)
} |
I'm OK with that kind of change. |
Change https://go.dev/cl/587918 mentions this issue: |
Change https://go.dev/cl/588076 mentions this issue: |
I think this needs more discussion. If I’m saying wrong, please let me know. |
Whoever calls Setrlimit will return an error in this case. They will know they are not able to set the limits. In our (runc) use case, we just need to remove the cache. We can set the limit as well, but that's optional, since we will use unix.Prlimit as we always did to set the limit. To me, this is the best middle ground between Go and runc needs. PS we can discuss it further in opencontainers/runc#4290 (i'm going to add go tip to its CI). |
Yes, I think this is enough for runc, but not enough for golang. |
Soft > Hard |
I don't think it matters much. If |
I looked into the manual page of `setlimit(2)`, there are two errors we should consider:
```
EPERM An unprivileged process tried to raise the hard limit; the
CAP_SYS_RESOURCE capability is required to do this.
EPERM The caller tried to increase the hard RLIMIT_NOFILE limit
above the maximum defined by /proc/sys/fs/nr_open (see
proc(5))
```
I think these two errors are normal errors that could be met by users.
For example(Not in runc):
If someone try to raise as more hard limit as possible in the process, but he hits one of EPERMs error, in a big probability he will ignores this ordinary call error, becuase this is just a try.
But at this time, the fork/execing still will be success.
If we clear the cache in Setlimit after got an error, the forked sub process's original nofile rlimit will be wrong.
It will make the user confused.
So, if there is a chance, I still suggest to add a specific public API: runtime.ClearSyscallRlimitCache().
But If all people think that to clear the cache using Setrlimit is an accepetable way, I have no strong objection opinion.
------------------------------------------------------------------
发件人:Ian Lance Taylor ***@***.***>
发送时间:2024年5月24日(星期五) 10:25
***@***.***>
***@***.***>; ***@***.***>
主 题:Re: [golang/go] syscall: caching RLIMIT_NOFILE is wrong implementation (Issue #66797)
I don't think it matters much. If Setrlimit fails for an ordinary call, it's likely to also fail when it is called while fork/execing a child.
—
Reply to this email directly, view it on GitHub <#66797 (comment) >, or unsubscribe <https://github.com/notifications/unsubscribe-auth/AAF7IQBCUK55UBW3QQOW6NLZD2QJ7AVCNFSM6AAAAABGDYYRQKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMRYGM3TSMBWGY >.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
I am reluctant to add new API for this very special case. It is very unusual for a program to care about the rlimit for child processes. It is very unusual for a program to change its rlimit and fail. The new API is only required for the intersection of those two very unusual cases. |
Since the introduction of origRlimitNofileCache in CL 476097 the only way to disable restoring RLIMIT_NOFILE before calling execve syscall (os.StartProcess etc) is this: var r syscall.Rlimit syscall.Getrlimit(syscall.RLIMIT_NOFILE, &r) syscall.Setrlimit(syscall.RLIMIT_NOFILE, &r) The problem is, this only works when setrlimit syscall succeeds, which is not possible in some scenarios. Let's assume that if a user calls syscall.Setrlimit, they unconditionally want to disable restoring the original rlimit. For #66797. Change-Id: I20d0365df4bd6a5c3cc8c22b0c0db87a25b52746 Reviewed-on: https://go-review.googlesource.com/c/go/+/588076 Run-TryBot: Kirill Kolyshkin <kolyshkin@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> TryBot-Bypass: Ian Lance Taylor <iant@golang.org>
Go version
go version go1.20.13 linux/amd64
Output of
go env
in your module/workspace:What did you do?
RLIMIT_NOFILE is cached in the go runtime, and RLIMIT_NOFILE in the cache is restored for the child process before exec.
Assume that the system's default RLIMIT_NOFILE is 1024, and process A generates child process B through fork. B is a go application, so the go runtime will cache RLIMIT_NOFILE as 1024. When I set B's RLIMIT_NOFILE to 10000 through prlimit in A, and use exec.Command to create process C in B, C's RLIMIT_NOFILE is restored to 1024.
This behavior is incompatible with the operating system. It also caused problems with runc:
opencontainers/runc#4195
The problem originates from f5eef58
What did you see happen?
Reference opencontainers/runc#4195
What did you expect to see?
RLIMIT_NOFILE should be correctly inherited to child processes
The text was updated successfully, but these errors were encountered: