-
Notifications
You must be signed in to change notification settings - Fork 1.1k
oci: check pid before using it #3868
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
Conversation
|
/retest |
internal/oci/container.go
Outdated
| // the unix start time of the process, as found by psgo | ||
| // this is used to track whether the pid we have stored | ||
| // is the same as the same pid number on the host | ||
| psStartTime string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about pidStartTime ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah that's better. I was at a loss for a variable name :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
processStartTime is probably better than that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will have to be persisted in state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am thinking that we should probably separate the pid returned from runc state from the container start pid. runc state could change to zero once stopped. We can separately store the startTime, Pid as top level fields in crio state that never change once populated.
|
/retest |
Codecov Report
@@ Coverage Diff @@
## master #3868 +/- ##
==========================================
+ Coverage 40.41% 40.71% +0.29%
==========================================
Files 111 109 -2
Lines 8947 8970 +23
==========================================
+ Hits 3616 3652 +36
+ Misses 4999 4986 -13
Partials 332 332 |
fc5fabe to
641d4fe
Compare
|
/retest |
|
/retest |
|
integration tests are a runc problem fixed in opencontainers/runc#2479, e2e test failures are legit |
e9e10bf to
eaeae76
Compare
|
/retest |
|
/retest |
internal/oci/container.go
Outdated
| } | ||
|
|
||
| // getInitStartTime reads the kernel's /proc entry for stime for PID. | ||
| func getInitStartTime(pid int) (int, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The word Init in the function name is misleading, since the function doesn't care if the pid is that of init or not.
| // won't exist anyway. | ||
| pid, _ := ctr.pid() // nolint:errcheck | ||
| if pid > 0 { | ||
| netNsPath := fmt.Sprintf("/proc/%d/ns/net", pid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't we return the pinned path here? We can get rid of the pid check then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a couple of reasons:
primarily: we don't have direct access to the Sandbox object, which owns the namespaces. We'd have to ask the server what the sandbox is, and then ask the sandbox what the namespace is.
secondarily, is it possible to have a private net namespace? if so, the sandbox level namespace won't be the correct one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Atleast for the pinned ns case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no private net namespaces in a pod. All the containers always share the pod network namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, well we still have the access problem. I would replumb this as a follow up, if we'd like
having exec sync update state each time is a bit excessive. In addition to exec'ing extra, it causes potential for runc state to flake, causing the container to go down. instead, we should just check if the pid is running, and proceed if so Signed-off-by: Peter Hunt <[email protected]>
in any case where we want to directly manipulate a pid on the host. this is unsafe, as we can encounter pid wrap. for those cases, we need to check the pid is the one we want to access, whether it's creating a namespace path with /proc or directly killing a container also, add unit tests for this fix, as well as refactor a few of the oci tests Signed-off-by: Peter Hunt <[email protected]>
9c32a5e to
ee7a9a0
Compare
|
@kolyshkin pointed out that most of findprocess is now obsolete with the existence of verifyPid(). I have removed it and associated calls |
now, verifyPid does all of the juicy bits of findprocess, without any of the overhead. Further, it added a lot of code that was largely written as a compatibility layer for windows. Since we have no intention in the near future to support windows anymore, let's drop it Signed-off-by: Peter Hunt <[email protected]>
|
/retest |
| // is valid. If not, infraPid should be less than or equal to 0 | ||
| if ns == nil || ns.Get() == nil { | ||
| if infraPid >= 0 { | ||
| if infraPid > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't match the new comment on 319. Did you mean to remove the = here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the comment is meant to say "if it's not valid". is the wording unclear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also had to read this part twice, and it is correct, including the comment, but maybe not super clear. Not sure how to improve it though :(
internal/oci/container.go
Outdated
| return false, nil | ||
| } | ||
|
|
||
| process, err := findprocess.FindProcess(pid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not suggesting to drop process.Release(), but drop both find and release. We have already checked the process is there, I'm not sure if it makes sense to do it one more time.
| // is valid. If not, infraPid should be less than or equal to 0 | ||
| if ns == nil || ns.Get() == nil { | ||
| if infraPid >= 0 { | ||
| if infraPid > 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also had to read this part twice, and it is correct, including the comment, but maybe not super clear. Not sure how to improve it though :(
|
|
||
| cState := c.State() | ||
| if !(cState.Status == oci.ContainerStateRunning || cState.Status == oci.ContainerStateCreated) { | ||
| if !c.IsAlive() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe
if _, err := c.verifyPid(); err != nil {and remove the whole IsAlive function?
Or, use IsAlive in all the places where you're using the if like the one in this comment. Otherwise it's a duplicated functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the nice thing about the different functions has to do with error handling. In the case of IsAlive, I believe it should be used in cases we presume the container to be alive, and it would be an error if it was not.
Conversely, verifyPid does not make an assertion of what the liveliness of the container should be
For instance, all uses in runtime_oci.go assume the container will eventually not be alive. in that case, we wouldn't want to print an error that the container wasn't alive
I think it is correct to error in server/container_execsync.go, as the client asked to exec in a container that is not running. Propagating the reason for this failure (pid wrap, container exited, we exec'ed too early somehow) is useful.
Do you have suggestions on how to balance these two use cases and error scenerios @kolyshkin
|
/retest |
|
/test e2e-aws |
|
/retest |
|
/lgtm |
|
/retest |
|
/retest |
2 similar comments
|
/retest |
|
/retest |
What type of PR is this?
/kind bug
What this PR does / why we need it:
As we found in the conmonmon saga, directly accessing pids on the host is a bit dangerous. Primarily, we risk believing a pid is the container process, when it is really a new process, because of pid wrap. While this is really unlikely to happen if you set it to the kernel pid_max (4 billion?), it still can happen, and can cause security problems (especially when using it for namespaces, when not managing namespace lifecycle)
Now, we drop every direct c.state.Pid access in favor of a helper function c.Pid() (and children). This allows us to do the proper verification (checking stime is the same for the pid we're trying to use as the container's pid originally was)
This PR also refactors how we use FindProcess(), as there was a bit of duplication, and direct accesses to c.state.Pid were littered in code associated with FindProcess()
This also carries the exec sync patch checking c.IsRunning() instead of updating container state, as a bonus.
TODO:
- [ ] maybe add this pid handling stuff in a new package for better unit testingBut I'm throwing this up to see how CI likes it
Which issue(s) this PR fixes:
Special notes for your reviewer:
Does this PR introduce a user-facing change?