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  }