github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/cmd/mitigate.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 package cmd 16 17 import ( 18 "context" 19 "fmt" 20 "io/ioutil" 21 "runtime" 22 23 "github.com/google/subcommands" 24 "github.com/SagerNet/gvisor/pkg/log" 25 "github.com/SagerNet/gvisor/runsc/flag" 26 "github.com/SagerNet/gvisor/runsc/mitigate" 27 ) 28 29 const ( 30 // cpuInfo is the path used to parse CPU info. 31 cpuInfo = "/proc/cpuinfo" 32 // allPossibleCPUs is the path used to enable CPUs. 33 allPossibleCPUs = "/sys/devices/system/cpu/possible" 34 ) 35 36 // Mitigate implements subcommands.Command for the "mitigate" command. 37 type Mitigate struct { 38 // Run the command without changing the underlying system. 39 dryRun bool 40 // Reverse mitigate by turning on all CPU cores. 41 reverse bool 42 // Path to file to read to create CPUSet. 43 path string 44 // Extra data for post mitigate operations. 45 data string 46 } 47 48 // Name implements subcommands.command.name. 49 func (*Mitigate) Name() string { 50 return "mitigate" 51 } 52 53 // Synopsis implements subcommands.Command.Synopsis. 54 func (*Mitigate) Synopsis() string { 55 return "mitigate mitigates the underlying system against side channel attacks" 56 } 57 58 // Usage implements Usage for cmd.Mitigate. 59 func (m Mitigate) Usage() string { 60 return fmt.Sprintf(`mitigate [flags] 61 62 mitigate mitigates a system to the "MDS" vulnerability by implementing a manual shutdown of SMT. The command checks /proc/cpuinfo for cpus having the MDS vulnerability, and if found, shutdown all but one CPU per hyperthread pair via /sys/devices/system/cpu/cpu{N}/online. CPUs can be restored by writing "2" to each file in /sys/devices/system/cpu/cpu{N}/online or performing a system reboot. 63 64 The command can be reversed with --reverse, which reads the total CPUs from /sys/devices/system/cpu/possible and enables all with /sys/devices/system/cpu/cpu{N}/online.%s`, m.usage()) 65 } 66 67 // SetFlags sets flags for the command Mitigate. 68 func (m *Mitigate) SetFlags(f *flag.FlagSet) { 69 f.BoolVar(&m.dryRun, "dryrun", false, "run the command without changing system") 70 f.BoolVar(&m.reverse, "reverse", false, "reverse mitigate by enabling all CPUs") 71 m.setFlags(f) 72 } 73 74 // Execute implements subcommands.Command.Execute. 75 func (m *Mitigate) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus { 76 if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" { 77 log.Warningf("As ARM is not affected by MDS, mitigate does not support") 78 return subcommands.ExitFailure 79 } 80 81 if f.NArg() != 0 { 82 f.Usage() 83 return subcommands.ExitUsageError 84 } 85 86 m.path = cpuInfo 87 if m.reverse { 88 m.path = allPossibleCPUs 89 } 90 91 set, err := m.doExecute() 92 if err != nil { 93 return Errorf("Execute failed: %v", err) 94 } 95 96 if m.data == "" { 97 return subcommands.ExitSuccess 98 } 99 100 if err = m.postMitigate(set); err != nil { 101 return Errorf("Post Mitigate failed: %v", err) 102 } 103 104 return subcommands.ExitSuccess 105 } 106 107 // Execute executes the Mitigate command. 108 func (m *Mitigate) doExecute() (mitigate.CPUSet, error) { 109 if m.dryRun { 110 log.Infof("Running with DryRun. No cpu settings will be changed.") 111 } 112 data, err := ioutil.ReadFile(m.path) 113 if err != nil { 114 return nil, fmt.Errorf("failed to read %s: %w", m.path, err) 115 } 116 if m.reverse { 117 set, err := m.doReverse(data) 118 if err != nil { 119 return nil, fmt.Errorf("reverse operation failed: %w", err) 120 } 121 return set, nil 122 } 123 set, err := m.doMitigate(data) 124 if err != nil { 125 return nil, fmt.Errorf("mitigate operation failed: %w", err) 126 } 127 return set, nil 128 } 129 130 func (m *Mitigate) doMitigate(data []byte) (mitigate.CPUSet, error) { 131 set, err := mitigate.NewCPUSet(data) 132 if err != nil { 133 return nil, err 134 } 135 136 log.Infof("Mitigate found the following CPUs...") 137 log.Infof("%s", set) 138 139 disableList := set.GetShutdownList() 140 log.Infof("Disabling threads on thread pairs.") 141 for _, t := range disableList { 142 log.Infof("Disable thread: %s", t) 143 if m.dryRun { 144 continue 145 } 146 if err := t.Disable(); err != nil { 147 return nil, fmt.Errorf("error disabling thread: %s err: %w", t, err) 148 } 149 } 150 log.Infof("Shutdown successful.") 151 return set, nil 152 } 153 154 func (m *Mitigate) doReverse(data []byte) (mitigate.CPUSet, error) { 155 set, err := mitigate.NewCPUSetFromPossible(data) 156 if err != nil { 157 return nil, err 158 } 159 160 log.Infof("Reverse mitigate found the following CPUs...") 161 log.Infof("%s", set) 162 163 enableList := set.GetRemainingList() 164 165 log.Infof("Enabling all CPUs...") 166 for _, t := range enableList { 167 log.Infof("Enabling thread: %s", t) 168 if m.dryRun { 169 continue 170 } 171 if err := t.Enable(); err != nil { 172 return nil, fmt.Errorf("error enabling thread: %s err: %w", t, err) 173 } 174 } 175 log.Infof("Enable successful.") 176 return set, nil 177 }