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