mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
d22dedf3d5
Previously we were only looking at stdin, which could be a pty but other stdio fds might be redirected. In that case, we can incorrectly end up using the stdin fd as *the* console fd, and sending all stdout/stderr to that FD, ignoring the redirect. Note that the behavior was actually flaky because the mechanism for choosing which stdio fd to treat as *the* pty fd is non-deterministic (due to the map iteration in fdimport/fdimport.go:Import) and so sometimes we would choose the correct one. This CL also cleans up `argsFromProcess` and `argsFromCLI`, which were setting their `FilePayload` unnecessarily, since it is always set in `Execute`. Fixes #11350 Fixes #11349 PiperOrigin-RevId: 716733446
225 lines
7.2 KiB
Go
225 lines
7.2 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
|
"gvisor.dev/gvisor/pkg/sentry/control"
|
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
|
)
|
|
|
|
func TestUser(t *testing.T) {
|
|
testCases := []struct {
|
|
input string
|
|
want user
|
|
wantErr bool
|
|
}{
|
|
{input: "0", want: user{kuid: 0, kuidSet: true, kgid: 0, kgidSet: false}},
|
|
{input: "7", want: user{kuid: 7, kuidSet: true, kgid: 0, kgidSet: false}},
|
|
{input: "49:343", want: user{kuid: 49, kuidSet: true, kgid: 343, kgidSet: true}},
|
|
{input: "0:2401", want: user{kuid: 0, kuidSet: true, kgid: 2401, kgidSet: true}},
|
|
{input: "", wantErr: true},
|
|
{input: "foo", wantErr: true},
|
|
{input: ":123", wantErr: true},
|
|
{input: "1:2:3", wantErr: true},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
var u user
|
|
if err := u.Set(tc.input); err != nil && tc.wantErr {
|
|
// We got an error and wanted one.
|
|
continue
|
|
} else if err == nil && tc.wantErr {
|
|
t.Errorf("user.Set(%s): got no error, but wanted one", tc.input)
|
|
} else if err != nil && !tc.wantErr {
|
|
t.Errorf("user.Set(%s): got error %v, but wanted none", tc.input, err)
|
|
} else if u != tc.want {
|
|
t.Errorf("user.Set(%s): got %+v, but wanted %+v", tc.input, u, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCLIArgs(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
ex Exec
|
|
spec specs.Process
|
|
argv []string
|
|
expected control.ExecArgs
|
|
}{
|
|
{
|
|
name: "spec used by default",
|
|
ex: Exec{},
|
|
spec: specs.Process{
|
|
User: specs.User{UID: 2, GID: 2, AdditionalGids: []uint32{1, 2, 3}},
|
|
Capabilities: &specs.LinuxCapabilities{Bounding: []string{"CAP_DAC_OVERRIDE"}, Inheritable: []string{"CAP_DAC_OVERRIDE"}},
|
|
Cwd: "/foo/bar",
|
|
Env: []string{"FOO=bar"},
|
|
},
|
|
argv: []string{"ls", "/"},
|
|
expected: control.ExecArgs{
|
|
Argv: []string{"ls", "/"},
|
|
Envv: []string{"FOO=bar"},
|
|
WorkingDirectory: "/foo/bar",
|
|
KUID: 2,
|
|
KGID: 2,
|
|
ExtraKGIDs: []auth.KGID{1, 2, 3},
|
|
Capabilities: &auth.TaskCapabilities{
|
|
BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
InheritableCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "spec overridden by CLI",
|
|
ex: Exec{
|
|
cwd: "/baz",
|
|
user: user{kuid: 4, kuidSet: true, kgid: 4, kgidSet: true},
|
|
extraKGIDs: []string{"4", "5", "6"},
|
|
caps: []string{"CAP_DAC_READ_SEARCH"},
|
|
env: []string{"BAZ=new"},
|
|
},
|
|
spec: specs.Process{
|
|
User: specs.User{UID: 2, GID: 2, AdditionalGids: []uint32{1, 2, 3}},
|
|
Capabilities: &specs.LinuxCapabilities{Bounding: []string{"CAP_DAC_OVERRIDE"}, Inheritable: []string{"CAP_DAC_OVERRIDE"}},
|
|
Cwd: "/foo/bar",
|
|
Env: []string{"FOO=bar"},
|
|
},
|
|
argv: []string{"ls", "/"},
|
|
expected: control.ExecArgs{
|
|
Argv: []string{"ls", "/"},
|
|
Envv: []string{"FOO=bar", "BAZ=new"},
|
|
WorkingDirectory: "/baz",
|
|
KUID: 4,
|
|
KGID: 4,
|
|
ExtraKGIDs: []auth.KGID{1, 2, 3, 4, 5, 6},
|
|
Capabilities: &auth.TaskCapabilities{
|
|
BoundingCaps: auth.CapabilitySetOfMany([]linux.Capability{linux.CAP_DAC_OVERRIDE, linux.CAP_DAC_READ_SEARCH}),
|
|
EffectiveCaps: auth.CapabilitySetOfMany([]linux.Capability{linux.CAP_DAC_READ_SEARCH}),
|
|
PermittedCaps: auth.CapabilitySetOfMany([]linux.Capability{linux.CAP_DAC_READ_SEARCH}),
|
|
InheritableCaps: auth.CapabilitySetOfMany([]linux.Capability{linux.CAP_DAC_OVERRIDE}),
|
|
// TODO(gvisor.dev/issue/3166): Once ambient capabilities is
|
|
// supported, AmbientCaps should be CAP_DAC_READ_SEARCH.
|
|
AmbientCaps: 0,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
e, err := tc.ex.argsFromCLI(&tc.spec, tc.argv, true)
|
|
if err != nil {
|
|
t.Errorf("argsFromCLI(%+v): got error: %+v", tc.ex, err)
|
|
return
|
|
}
|
|
if diff := cmp.Diff(*e, tc.expected, cmpopts.IgnoreUnexported(os.File{})); diff != "" {
|
|
t.Errorf("argsFromCLI(%+v): diff (+want -got):\n%s", tc.ex, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestJSONArgs(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
ex Exec
|
|
spec specs.Process
|
|
p specs.Process
|
|
expected control.ExecArgs
|
|
}{
|
|
{
|
|
name: "flags overridden by process file",
|
|
ex: Exec{
|
|
cwd: "/baz/quux",
|
|
user: user{kuid: 1, kgid: 1},
|
|
extraKGIDs: []string{"4", "5", "6"},
|
|
caps: []string{"CAP_SETGID"},
|
|
processPath: "/bin/foo",
|
|
},
|
|
spec: specs.Process{
|
|
Capabilities: &specs.LinuxCapabilities{Bounding: []string{"CAP_DAC_READ_SEARCH"}},
|
|
},
|
|
p: specs.Process{
|
|
User: specs.User{UID: 0, GID: 0, AdditionalGids: []uint32{1, 2, 3}},
|
|
Args: []string{"ls", "/"},
|
|
Cwd: "/foo/bar",
|
|
Capabilities: &specs.LinuxCapabilities{
|
|
Bounding: []string{"CAP_DAC_OVERRIDE"},
|
|
Effective: []string{"CAP_DAC_OVERRIDE"},
|
|
Inheritable: []string{"CAP_DAC_OVERRIDE"},
|
|
Permitted: []string{"CAP_DAC_OVERRIDE"},
|
|
},
|
|
},
|
|
expected: control.ExecArgs{
|
|
Argv: []string{"ls", "/"},
|
|
WorkingDirectory: "/foo/bar",
|
|
KUID: 0,
|
|
KGID: 0,
|
|
ExtraKGIDs: []auth.KGID{1, 2, 3},
|
|
Capabilities: &auth.TaskCapabilities{
|
|
BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
EffectiveCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
InheritableCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
PermittedCaps: auth.CapabilitySetOf(linux.CAP_DAC_OVERRIDE),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "capabilities fallback to spec",
|
|
ex: Exec{},
|
|
spec: specs.Process{
|
|
Capabilities: &specs.LinuxCapabilities{
|
|
Bounding: []string{"CAP_DAC_READ_SEARCH"}},
|
|
},
|
|
p: specs.Process{
|
|
User: specs.User{UID: 0, GID: 0},
|
|
Args: []string{"ls", "/"},
|
|
Cwd: "/foo/bar",
|
|
// Does not specify capabilities.
|
|
},
|
|
expected: control.ExecArgs{
|
|
Argv: []string{"ls", "/"},
|
|
WorkingDirectory: "/foo/bar",
|
|
KUID: 0,
|
|
KGID: 0,
|
|
ExtraKGIDs: []auth.KGID{},
|
|
Capabilities: &auth.TaskCapabilities{
|
|
BoundingCaps: auth.CapabilitySetOf(linux.CAP_DAC_READ_SEARCH),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
e, err := argsFromProcess(&tc.spec, &tc.p, true)
|
|
if err != nil {
|
|
t.Errorf("argsFromProcess(%+v): got error: %+v", tc.p, err)
|
|
return
|
|
}
|
|
if diff := cmp.Diff(*e, tc.expected, cmpopts.IgnoreUnexported(os.File{})); diff != "" {
|
|
t.Errorf("argsFromProcess(%+v): diff (+want -got):\n%s", tc.p, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|