From 775a321120f09420ef37ba9455371f193380a695 Mon Sep 17 00:00:00 2001 From: Jamie Liu Date: Fri, 3 Sep 2021 19:07:12 -0700 Subject: Add //pkg/sentry/seccheck. This defines common infrastructure for dynamically-configured security checks, including an example usage in the clone(2) path. PiperOrigin-RevId: 394797270 --- pkg/sentry/seccheck/seccheck_test.go | 157 +++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 pkg/sentry/seccheck/seccheck_test.go (limited to 'pkg/sentry/seccheck/seccheck_test.go') diff --git a/pkg/sentry/seccheck/seccheck_test.go b/pkg/sentry/seccheck/seccheck_test.go new file mode 100644 index 000000000..687810d18 --- /dev/null +++ b/pkg/sentry/seccheck/seccheck_test.go @@ -0,0 +1,157 @@ +// Copyright 2021 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 seccheck + +import ( + "errors" + "testing" + + "gvisor.dev/gvisor/pkg/context" +) + +type testChecker struct { + CheckerDefaults + + onClone func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error +} + +// Clone implements Checker.Clone. +func (c *testChecker) Clone(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + if c.onClone == nil { + return nil + } + return c.onClone(ctx, mask, info) +} + +func TestNoChecker(t *testing.T) { + var s state + if s.Enabled(PointClone) { + t.Errorf("Enabled(PointClone): got true, wanted false") + } +} + +func TestCheckerNotRegisteredForPoint(t *testing.T) { + var s state + s.AppendChecker(&testChecker{}, &CheckerReq{}) + if s.Enabled(PointClone) { + t.Errorf("Enabled(PointClone): got true, wanted false") + } +} + +func TestCheckerRegistered(t *testing.T) { + var s state + checkerCalled := false + s.AppendChecker(&testChecker{onClone: func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + checkerCalled = true + return nil + }}, &CheckerReq{ + Points: []Point{PointClone}, + Clone: CloneFields{ + Credentials: true, + }, + }) + + if !s.Enabled(PointClone) { + t.Errorf("Enabled(PointClone): got false, wanted true") + } + if !s.CloneReq().Contains(CloneFieldCredentials) { + t.Errorf("CloneReq().Contains(CloneFieldCredentials): got false, wanted true") + } + if err := s.Clone(context.Background(), CloneFieldSet{}, &CloneInfo{}); err != nil { + t.Errorf("Clone(): got %v, wanted nil", err) + } + if !checkerCalled { + t.Errorf("Clone() did not call Checker.Clone()") + } +} + +func TestMultipleCheckersRegistered(t *testing.T) { + var s state + checkersCalled := [2]bool{} + s.AppendChecker(&testChecker{onClone: func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + checkersCalled[0] = true + return nil + }}, &CheckerReq{ + Points: []Point{PointClone}, + Clone: CloneFields{ + Args: true, + }, + }) + s.AppendChecker(&testChecker{onClone: func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + checkersCalled[1] = true + return nil + }}, &CheckerReq{ + Points: []Point{PointClone}, + Clone: CloneFields{ + Created: TaskFields{ + ThreadID: true, + }, + }, + }) + + if !s.Enabled(PointClone) { + t.Errorf("Enabled(PointClone): got false, wanted true") + } + // CloneReq() should return the union of requested fields from all calls to + // AppendChecker. + req := s.CloneReq() + if !req.Contains(CloneFieldArgs) { + t.Errorf("req.Contains(CloneFieldArgs): got false, wanted true") + } + if !req.Created.Contains(TaskFieldThreadID) { + t.Errorf("req.Created.Contains(TaskFieldThreadID): got false, wanted true") + } + if err := s.Clone(context.Background(), CloneFieldSet{}, &CloneInfo{}); err != nil { + t.Errorf("Clone(): got %v, wanted nil", err) + } + for i := range checkersCalled { + if !checkersCalled[i] { + t.Errorf("Clone() did not call Checker.Clone() index %d", i) + } + } +} + +func TestCheckpointReturnsFirstCheckerError(t *testing.T) { + errFirstChecker := errors.New("first Checker error") + errSecondChecker := errors.New("second Checker error") + + var s state + checkersCalled := [2]bool{} + s.AppendChecker(&testChecker{onClone: func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + checkersCalled[0] = true + return errFirstChecker + }}, &CheckerReq{ + Points: []Point{PointClone}, + }) + s.AppendChecker(&testChecker{onClone: func(ctx context.Context, mask CloneFieldSet, info CloneInfo) error { + checkersCalled[1] = true + return errSecondChecker + }}, &CheckerReq{ + Points: []Point{PointClone}, + }) + + if !s.Enabled(PointClone) { + t.Errorf("Enabled(PointClone): got false, wanted true") + } + if err := s.Clone(context.Background(), CloneFieldSet{}, &CloneInfo{}); err != errFirstChecker { + t.Errorf("Clone(): got %v, wanted %v", err, errFirstChecker) + } + if !checkersCalled[0] { + t.Errorf("Clone() did not call first Checker") + } + if checkersCalled[1] { + t.Errorf("Clone() called second Checker") + } +} -- cgit v1.2.3