github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/runsc/boot/compat.go (about) 1 // Copyright 2018 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 boot 16 17 import ( 18 "fmt" 19 "os" 20 21 "golang.org/x/sys/unix" 22 "google.golang.org/protobuf/proto" 23 "github.com/SagerNet/gvisor/pkg/eventchannel" 24 "github.com/SagerNet/gvisor/pkg/log" 25 rpb "github.com/SagerNet/gvisor/pkg/sentry/arch/registers_go_proto" 26 ucspb "github.com/SagerNet/gvisor/pkg/sentry/kernel/uncaught_signal_go_proto" 27 "github.com/SagerNet/gvisor/pkg/sentry/strace" 28 spb "github.com/SagerNet/gvisor/pkg/sentry/unimpl/unimplemented_syscall_go_proto" 29 "github.com/SagerNet/gvisor/pkg/sync" 30 ) 31 32 func initCompatLogs(fd int) error { 33 ce, err := newCompatEmitter(fd) 34 if err != nil { 35 return err 36 } 37 eventchannel.AddEmitter(ce) 38 return nil 39 } 40 41 type compatEmitter struct { 42 sink *log.BasicLogger 43 nameMap strace.SyscallMap 44 45 // mu protects the fields below. 46 mu sync.Mutex 47 48 // trackers map syscall number to the respective tracker instance. 49 // Protected by 'mu'. 50 trackers map[uint64]syscallTracker 51 } 52 53 func newCompatEmitter(logFD int) (*compatEmitter, error) { 54 nameMap, ok := getSyscallNameMap() 55 if !ok { 56 return nil, fmt.Errorf("syscall table not found") 57 } 58 59 c := &compatEmitter{ 60 // Always logs to default logger. 61 sink: log.Log(), 62 nameMap: nameMap, 63 trackers: make(map[uint64]syscallTracker), 64 } 65 66 if logFD > 0 { 67 f := os.NewFile(uintptr(logFD), "user log file") 68 target := &log.MultiEmitter{c.sink, log.K8sJSONEmitter{&log.Writer{Next: f}}} 69 c.sink = &log.BasicLogger{Level: log.Info, Emitter: target} 70 } 71 return c, nil 72 } 73 74 // Emit implements eventchannel.Emitter. 75 func (c *compatEmitter) Emit(msg proto.Message) (bool, error) { 76 switch m := msg.(type) { 77 case *spb.UnimplementedSyscall: 78 c.emitUnimplementedSyscall(m) 79 case *ucspb.UncaughtSignal: 80 c.emitUncaughtSignal(m) 81 } 82 83 return false, nil 84 } 85 86 func (c *compatEmitter) emitUnimplementedSyscall(us *spb.UnimplementedSyscall) { 87 regs := us.Registers 88 89 c.mu.Lock() 90 defer c.mu.Unlock() 91 92 sysnr := syscallNum(regs) 93 tr := c.trackers[sysnr] 94 if tr == nil { 95 switch sysnr { 96 case unix.SYS_PRCTL: 97 // args: cmd, ... 98 tr = newArgsTracker(0) 99 100 case unix.SYS_IOCTL, unix.SYS_EPOLL_CTL, unix.SYS_SHMCTL, unix.SYS_FUTEX, unix.SYS_FALLOCATE: 101 // args: fd/addr, cmd, ... 102 tr = newArgsTracker(1) 103 104 case unix.SYS_GETSOCKOPT, unix.SYS_SETSOCKOPT: 105 // args: fd, level, name, ... 106 tr = newArgsTracker(1, 2) 107 108 case unix.SYS_SEMCTL: 109 // args: semid, semnum, cmd, ... 110 tr = newArgsTracker(2) 111 112 default: 113 tr = newArchArgsTracker(sysnr) 114 if tr == nil { 115 tr = &onceTracker{} 116 } 117 } 118 c.trackers[sysnr] = tr 119 } 120 121 if tr.shouldReport(regs) { 122 name := c.nameMap.Name(uintptr(sysnr)) 123 c.sink.Infof("Unsupported syscall %s(%#x,%#x,%#x,%#x,%#x,%#x). It is "+ 124 "likely that you can safely ignore this message and that this is not "+ 125 "the cause of any error. Please, refer to %s/%s for more information.", 126 name, argVal(0, regs), argVal(1, regs), argVal(2, regs), argVal(3, regs), 127 argVal(4, regs), argVal(5, regs), syscallLink, name) 128 129 tr.onReported(regs) 130 } 131 } 132 133 func (c *compatEmitter) emitUncaughtSignal(msg *ucspb.UncaughtSignal) { 134 sig := unix.Signal(msg.SignalNumber) 135 c.sink.Infof( 136 "Uncaught signal: %q (%d), PID: %d, TID: %d, fault addr: %#x", 137 sig, msg.SignalNumber, msg.Pid, msg.Tid, msg.FaultAddr) 138 } 139 140 // Close implements eventchannel.Emitter. 141 func (c *compatEmitter) Close() error { 142 c.sink = nil 143 return nil 144 } 145 146 // syscallTracker interface allows filters to apply differently depending on 147 // the syscall and arguments. 148 type syscallTracker interface { 149 // shouldReport returns true is the syscall should be reported. 150 shouldReport(regs *rpb.Registers) bool 151 152 // onReported marks the syscall as reported. 153 onReported(regs *rpb.Registers) 154 } 155 156 // onceTracker reports only a single time, used for most syscalls. 157 type onceTracker struct { 158 reported bool 159 } 160 161 func (o *onceTracker) shouldReport(_ *rpb.Registers) bool { 162 return !o.reported 163 } 164 165 func (o *onceTracker) onReported(_ *rpb.Registers) { 166 o.reported = true 167 } 168 169 // argsTracker reports only once for each different combination of arguments. 170 // It's used for generic syscalls like ioctl to report once per 'cmd'. 171 type argsTracker struct { 172 // argsIdx is the syscall arguments to use as unique ID. 173 argsIdx []int 174 reported map[string]struct{} 175 count int 176 } 177 178 func newArgsTracker(argIdx ...int) *argsTracker { 179 return &argsTracker{argsIdx: argIdx, reported: make(map[string]struct{})} 180 } 181 182 // key returns the command based on the syscall argument index. 183 func (a *argsTracker) key(regs *rpb.Registers) string { 184 var rv string 185 for _, idx := range a.argsIdx { 186 rv += fmt.Sprintf("%d|", argVal(idx, regs)) 187 } 188 return rv 189 } 190 191 func (a *argsTracker) shouldReport(regs *rpb.Registers) bool { 192 if a.count >= reportLimit { 193 return false 194 } 195 _, ok := a.reported[a.key(regs)] 196 return !ok 197 } 198 199 func (a *argsTracker) onReported(regs *rpb.Registers) { 200 a.count++ 201 a.reported[a.key(regs)] = struct{}{} 202 }