github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/cmd/mitigate_test.go (about)

     1  // Copyright 2021 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build amd64
    16  
    17  package cmd
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/SagerNet/gvisor/runsc/mitigate/mock"
    27  )
    28  
    29  type executeTestCase struct {
    30  	name          string
    31  	mitigateData  string
    32  	mitigateError error
    33  	mitigateCPU   int
    34  	reverseData   string
    35  	reverseError  error
    36  	reverseCPU    int
    37  }
    38  
    39  func TestExecute(t *testing.T) {
    40  
    41  	partial := `processor       : 1
    42  vendor_id       : AuthenticAMD
    43  cpu family      : 23
    44  model           : 49
    45  model name      : AMD EPYC 7B12
    46  physical id     : 0
    47  bugs         : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
    48  power management:
    49  `
    50  
    51  	for _, tc := range []executeTestCase{
    52  		{
    53  			name:         "CascadeLake4",
    54  			mitigateData: mock.CascadeLake4.MakeCPUString(),
    55  			mitigateCPU:  2,
    56  			reverseData:  mock.CascadeLake4.MakeSysPossibleString(),
    57  			reverseCPU:   4,
    58  		},
    59  		{
    60  			name:          "Empty",
    61  			mitigateData:  "",
    62  			mitigateError: fmt.Errorf(`mitigate operation failed: no cpus found for: ""`),
    63  			reverseData:   "",
    64  			reverseError:  fmt.Errorf(`reverse operation failed: mismatch regex from possible: ""`),
    65  		},
    66  		{
    67  			name: "Partial",
    68  			mitigateData: `processor       : 0
    69  vendor_id       : AuthenticAMD
    70  cpu family      : 23
    71  model           : 49
    72  model name      : AMD EPYC 7B12
    73  physical id     : 0
    74  core id         : 0
    75  cpu cores       : 1
    76  bugs            : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
    77  power management::84
    78  
    79  ` + partial,
    80  			mitigateError: fmt.Errorf(`mitigate operation failed: failed to match key "core id": %q`, partial),
    81  			reverseData:   "1-",
    82  			reverseError:  fmt.Errorf(`reverse operation failed: mismatch regex from possible: %q`, "1-"),
    83  		},
    84  	} {
    85  		t.Run(tc.name, func(t *testing.T) {
    86  			m := &Mitigate{
    87  				dryRun: true,
    88  			}
    89  			m.doExecuteTest(t, "Mitigate", tc.mitigateData, tc.mitigateCPU, tc.mitigateError)
    90  
    91  			m.reverse = true
    92  			m.doExecuteTest(t, "Reverse", tc.reverseData, tc.reverseCPU, tc.reverseError)
    93  		})
    94  	}
    95  }
    96  
    97  func TestExecuteSmoke(t *testing.T) {
    98  	smokeMitigate, err := ioutil.ReadFile(cpuInfo)
    99  	if err != nil {
   100  		t.Fatalf("Failed to read %s: %v", cpuInfo, err)
   101  	}
   102  
   103  	m := &Mitigate{
   104  		dryRun: true,
   105  	}
   106  
   107  	m.doExecuteTest(t, "Mitigate", string(smokeMitigate), 0, nil)
   108  
   109  	smokeReverse, err := ioutil.ReadFile(allPossibleCPUs)
   110  	if err != nil {
   111  		t.Fatalf("Failed to read %s: %v", allPossibleCPUs, err)
   112  	}
   113  
   114  	m.reverse = true
   115  	m.doExecuteTest(t, "Reverse", string(smokeReverse), 0, nil)
   116  }
   117  
   118  // doExecuteTest runs Execute with the mitigate operation and reverse operation.
   119  func (m *Mitigate) doExecuteTest(t *testing.T, name, data string, want int, wantErr error) {
   120  	t.Run(name, func(t *testing.T) {
   121  		file, err := ioutil.TempFile("", "outfile.txt")
   122  		if err != nil {
   123  			t.Fatalf("Failed to create tmpfile: %v", err)
   124  		}
   125  		defer os.Remove(file.Name())
   126  
   127  		if _, err := file.WriteString(data); err != nil {
   128  			t.Fatalf("Failed to write to file: %v", err)
   129  		}
   130  
   131  		// Set fields for mitigate and dryrun to keep test hermetic.
   132  		m.path = file.Name()
   133  
   134  		set, err := m.doExecute()
   135  		if err = checkErr(wantErr, err); err != nil {
   136  			t.Fatalf("Mitigate error mismatch: %v", err)
   137  		}
   138  
   139  		// case where test should end in error or we don't care
   140  		// about how many cpus are returned.
   141  		if wantErr != nil || want < 1 {
   142  			return
   143  		}
   144  		got := len(set.GetRemainingList())
   145  		if want != got {
   146  			t.Fatalf("Failed wrong number of remaining CPUs: want %d, got %d", want, got)
   147  		}
   148  
   149  	})
   150  }
   151  
   152  // checkErr checks error for equality.
   153  func checkErr(want, got error) error {
   154  	switch {
   155  	case want == nil && got == nil:
   156  	case want != nil && got == nil:
   157  		fallthrough
   158  	case want == nil && got != nil:
   159  		fallthrough
   160  	case want.Error() != strings.Trim(got.Error(), " "):
   161  		return fmt.Errorf("got: %v want: %v", got, want)
   162  	}
   163  	return nil
   164  }