gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/mitigate/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  //go:build amd64
    16  // +build amd64
    17  
    18  package mitigate
    19  
    20  import (
    21  	"io/ioutil"
    22  	"strings"
    23  	"testing"
    24  )
    25  
    26  // TestMockCPUSet tests mock cpu test cases against the cpuSet functions.
    27  func TestMockCPUSet(t *testing.T) {
    28  	for _, tc := range []struct {
    29  		testCase     MockCPU
    30  		isVulnerable bool
    31  	}{
    32  		{
    33  			testCase:     AMD8,
    34  			isVulnerable: false,
    35  		},
    36  		{
    37  			testCase:     Haswell2,
    38  			isVulnerable: true,
    39  		},
    40  		{
    41  			testCase:     Haswell2core,
    42  			isVulnerable: true,
    43  		},
    44  		{
    45  			testCase:     CascadeLake2,
    46  			isVulnerable: true,
    47  		},
    48  		{
    49  			testCase:     CascadeLake4,
    50  			isVulnerable: true,
    51  		},
    52  	} {
    53  		t.Run(tc.testCase.Name, func(t *testing.T) {
    54  			data := tc.testCase.MakeCPUSet().String()
    55  			set, err := NewCPUSet(data)
    56  			if err != nil {
    57  				t.Fatalf("Failed to create cpuSet: %v", err)
    58  			}
    59  
    60  			if tc.testCase.NumCPUs() != len(set) {
    61  				t.Fatalf("Got wrong number of CPUs: want: %d got: %d", tc.testCase.NumCPUs(), len(set))
    62  			}
    63  
    64  			if set.IsVulnerable() != tc.isVulnerable {
    65  				t.Fatalf("incorrect vulnerable value: got: %t want: %t", set.IsVulnerable(), tc.isVulnerable)
    66  			}
    67  			t.Logf("data: %s", data)
    68  		})
    69  	}
    70  }
    71  
    72  // TestGetCPU tests basic parsing of single CPU strings from reading
    73  // /proc/cpuinfo.
    74  func TestGetCPU(t *testing.T) {
    75  	data := `processor	: 0
    76  vendor_id	: GenuineIntel
    77  cpu family	: 6
    78  model		: 85
    79  physical id: 0
    80  core id		: 0
    81  bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa itlb_multihit
    82  `
    83  	want := CPU{
    84  		processorNumber: 0,
    85  		vendorID:        "GenuineIntel",
    86  		cpuFamily:       6,
    87  		model:           85,
    88  		physicalID:      0,
    89  		coreID:          0,
    90  		bugs: map[string]struct{}{
    91  			"cpu_meltdown":      {},
    92  			"spectre_v1":        {},
    93  			"spectre_v2":        {},
    94  			"spec_store_bypass": {},
    95  			"l1tf":              {},
    96  			"mds":               {},
    97  			"swapgs":            {},
    98  			"taa":               {},
    99  			"itlb_multihit":     {},
   100  		},
   101  	}
   102  
   103  	got, err := newCPU(data)
   104  	if err != nil {
   105  		t.Fatalf("getCpu failed with error: %v", err)
   106  	}
   107  
   108  	if !want.SimilarTo(got) {
   109  		t.Fatalf("Failed cpus not similar: got: %+v, want: %+v", got, want)
   110  	}
   111  
   112  	if !got.IsVulnerable() {
   113  		t.Fatalf("Failed: cpu should be vulnerable.")
   114  	}
   115  }
   116  
   117  func TestInvalid(t *testing.T) {
   118  	result, err := newCPU(`something not a processor`)
   119  	if err == nil {
   120  		t.Fatalf("getCPU set didn't return an error: %+v", result)
   121  	}
   122  
   123  	if !strings.Contains(err.Error(), "failed to match key \"processor\"") {
   124  		t.Fatalf("Incorrect error returned: %v", err)
   125  	}
   126  }
   127  
   128  // TestCPUSet tests getting the right number of CPUs from
   129  // parsing full output of /proc/cpuinfo.
   130  func TestCPUSet(t *testing.T) {
   131  	data := `processor	: 0
   132  vendor_id	: GenuineIntel
   133  cpu family	: 6
   134  model		: 63
   135  model name	: Intel(R) Xeon(R) CPU @ 2.30GHz
   136  stepping	: 0
   137  microcode	: 0x1
   138  cpu MHz		: 2299.998
   139  cache size	: 46080 KB
   140  physical id	: 0
   141  siblings	: 2
   142  core id		: 0
   143  cpu cores	: 1
   144  apicid		: 0
   145  initial apicid	: 0
   146  fpu		: yes
   147  fpu_exception	: yes
   148  cpuid level	: 13
   149  wp		: yes
   150  flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
   151  bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
   152  bogomips	: 4599.99
   153  clflush size	: 64
   154  cache_alignment	: 64
   155  address sizes	: 46 bits physical, 48 bits virtual
   156  power management:
   157  
   158  processor	: 1
   159  vendor_id	: GenuineIntel
   160  cpu family	: 6
   161  model		: 63
   162  model name	: Intel(R) Xeon(R) CPU @ 2.30GHz
   163  stepping	: 0
   164  microcode	: 0x1
   165  cpu MHz		: 2299.998
   166  cache size	: 46080 KB
   167  physical id	: 0
   168  siblings	: 2
   169  core id		: 0
   170  cpu cores	: 1
   171  apicid		: 1
   172  initial apicid	: 1
   173  fpu		: yes
   174  fpu_exception	: yes
   175  cpuid level	: 13
   176  wp		: yes
   177  flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
   178  bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
   179  bogomips	: 4599.99
   180  clflush size	: 64
   181  cache_alignment	: 64
   182  address sizes	: 46 bits physical, 48 bits virtual
   183  power management:
   184  `
   185  	cpuSet, err := NewCPUSet(data)
   186  	if err != nil {
   187  		t.Fatalf("getCPUSet failed: %v", err)
   188  	}
   189  
   190  	wantCPULen := 2
   191  	if len(cpuSet) != wantCPULen {
   192  		t.Fatalf("Num CPU mismatch: want: %d, got: %d", wantCPULen, len(cpuSet))
   193  	}
   194  
   195  	wantCPU := CPU{
   196  		vendorID:  "GenuineIntel",
   197  		cpuFamily: 6,
   198  		model:     63,
   199  		bugs: map[string]struct{}{
   200  			"cpu_meltdown":      {},
   201  			"spectre_v1":        {},
   202  			"spectre_v2":        {},
   203  			"spec_store_bypass": {},
   204  			"l1tf":              {},
   205  			"mds":               {},
   206  			"swapgs":            {},
   207  		},
   208  	}
   209  
   210  	for _, c := range cpuSet {
   211  		if !wantCPU.SimilarTo(c) {
   212  			t.Fatalf("Failed cpus not equal: got: %+v, want: %+v", c, wantCPU)
   213  		}
   214  	}
   215  }
   216  
   217  // TestReadFile is a smoke test for parsing methods.
   218  func TestReadFile(t *testing.T) {
   219  	data, err := ioutil.ReadFile("/proc/cpuinfo")
   220  	if err != nil {
   221  		t.Fatalf("Failed to read cpuinfo: %v", err)
   222  	}
   223  
   224  	set, err := NewCPUSet(string(data))
   225  	if err != nil {
   226  		t.Fatalf("Failed to parse CPU data %v\n%s", err, data)
   227  	}
   228  
   229  	if len(set) < 1 {
   230  		t.Fatalf("Failed to parse any CPUs: %d", len(set))
   231  	}
   232  
   233  	t.Log(set)
   234  }
   235  
   236  // TestVulnerable tests if the isVulnerable method is correct
   237  // among known CPUs in GCP.
   238  func TestVulnerable(t *testing.T) {
   239  	const haswell = `processor       : 0
   240  vendor_id       : GenuineIntel
   241  cpu family      : 6
   242  model           : 63
   243  model name      : Intel(R) Xeon(R) CPU @ 2.30GHz
   244  stepping        : 0
   245  microcode       : 0x1
   246  cpu MHz         : 2299.998
   247  cache size      : 46080 KB
   248  physical id     : 0
   249  siblings        : 4
   250  core id         : 0
   251  cpu cores       : 2
   252  apicid          : 0
   253  initial apicid  : 0
   254  fpu             : yes
   255  fpu_exception   : yes
   256  cpuid level     : 13
   257  wp              : yes
   258  flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt arat md_clear arch_capabilities
   259  bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs
   260  bogomips        : 4599.99
   261  clflush size    : 64
   262  cache_alignment : 64
   263  address sizes   : 46 bits physical, 48 bits virtual
   264  power management:`
   265  
   266  	const skylake = `processor       : 0
   267  vendor_id       : GenuineIntel
   268  cpu family      : 6
   269  model           : 85
   270  model name      : Intel(R) Xeon(R) CPU @ 2.00GHz
   271  stepping        : 3
   272  microcode       : 0x1
   273  cpu MHz         : 2000.180
   274  cache size      : 39424 KB
   275  physical id     : 0
   276  siblings        : 2
   277  core id         : 0
   278  cpu cores       : 1
   279  apicid          : 0
   280  initial apicid  : 0
   281  fpu             : yes
   282  fpu_exception   : yes
   283  cpuid level     : 13
   284  wp              : yes
   285  flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch invpcid_single pti ssbd ibrs ibpb stibp fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm mpx avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat md_clear arch_capabilities
   286  bugs            : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs taa
   287  bogomips        : 4000.36
   288  clflush size    : 64
   289  cache_alignment : 64
   290  address sizes   : 46 bits physical, 48 bits virtual
   291  power management:`
   292  
   293  	const amd = `processor       : 0
   294  vendor_id       : AuthenticAMD
   295  cpu family      : 23
   296  model           : 49
   297  model name      : AMD EPYC 7B12
   298  stepping        : 0
   299  microcode       : 0x1000065
   300  cpu MHz         : 2250.000
   301  cache size      : 512 KB
   302  physical id     : 0
   303  siblings        : 2
   304  core id         : 0
   305  cpu cores       : 1
   306  apicid          : 0
   307  initial apicid  : 0
   308  fpu             : yes
   309  fpu_exception   : yes
   310  cpuid level     : 13
   311  wp              : yes
   312  flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb stibp vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsaveopt xsavec xgetbv1 clzero xsaveerptr arat npt nrip_save umip rdpid
   313  bugs            : sysret_ss_attrs spectre_v1 spectre_v2 spec_store_bypass
   314  bogomips        : 4500.00
   315  TLB size        : 3072 4K pages
   316  clflush size    : 64
   317  cache_alignment : 64
   318  address sizes   : 48 bits physical, 48 bits virtual
   319  power management:`
   320  
   321  	for _, tc := range []struct {
   322  		name       string
   323  		cpuString  string
   324  		vulnerable bool
   325  	}{
   326  		{
   327  			name:       "haswell",
   328  			cpuString:  haswell,
   329  			vulnerable: true,
   330  		}, {
   331  			name:       "skylake",
   332  			cpuString:  skylake,
   333  			vulnerable: true,
   334  		}, {
   335  			name:       "amd",
   336  			cpuString:  amd,
   337  			vulnerable: false,
   338  		},
   339  	} {
   340  		t.Run(tc.name, func(t *testing.T) {
   341  			set, err := NewCPUSet(tc.cpuString)
   342  			if err != nil {
   343  				t.Fatalf("Failed to getCPUSet:%v\n %s", err, tc.cpuString)
   344  			}
   345  
   346  			if len(set) < 1 {
   347  				t.Fatalf("Returned empty cpu set: %v", set)
   348  			}
   349  
   350  			for _, c := range set {
   351  				got := func() bool {
   352  					return c.IsVulnerable()
   353  				}()
   354  
   355  				if got != tc.vulnerable {
   356  					t.Fatalf("Mismatch vulnerable for cpu %s: got %t want: %t", tc.name, tc.vulnerable, got)
   357  				}
   358  			}
   359  		})
   360  	}
   361  }