gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  	"gvisor.dev/gvisor/pkg/eventchannel"
    24  	"gvisor.dev/gvisor/pkg/log"
    25  	rpb "gvisor.dev/gvisor/pkg/sentry/arch/registers_go_proto"
    26  	"gvisor.dev/gvisor/pkg/sentry/strace"
    27  	spb "gvisor.dev/gvisor/pkg/sentry/unimpl/unimplemented_syscall_go_proto"
    28  	"gvisor.dev/gvisor/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  }