summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--pkg/abi/linux/ioctl.go75
-rw-r--r--pkg/abi/linux/prctl.go99
-rw-r--r--pkg/sentry/fs/host/BUILD1
-rw-r--r--pkg/sentry/fs/host/tty.go30
-rw-r--r--pkg/sentry/fs/tty/BUILD1
-rw-r--r--pkg/sentry/fs/tty/master.go45
-rw-r--r--pkg/sentry/fs/tty/slave.go3
-rw-r--r--pkg/sentry/kernel/BUILD2
-rw-r--r--pkg/sentry/kernel/kernel.go17
-rw-r--r--pkg/sentry/kernel/pipe/reader_writer.go3
-rw-r--r--pkg/sentry/kernel/task.go3
-rw-r--r--pkg/sentry/socket/epsocket/BUILD1
-rw-r--r--pkg/sentry/socket/epsocket/epsocket.go3
-rw-r--r--pkg/sentry/socket/rpcinet/BUILD1
-rw-r--r--pkg/sentry/socket/rpcinet/socket.go5
-rw-r--r--pkg/sentry/syscalls/BUILD18
-rw-r--r--pkg/sentry/syscalls/linux/linux64.go2
-rw-r--r--pkg/sentry/syscalls/linux/sys_prctl.go39
-rw-r--r--pkg/sentry/syscalls/linux/sys_shm.go1
-rw-r--r--pkg/sentry/syscalls/linux/sys_tls.go3
-rw-r--r--pkg/sentry/syscalls/syscalls.go15
-rw-r--r--pkg/sentry/unimpl/BUILD30
-rw-r--r--pkg/sentry/unimpl/events.go45
-rw-r--r--pkg/sentry/unimpl/unimplemented_syscall.proto (renamed from pkg/sentry/syscalls/unimplemented_syscall.proto)0
-rw-r--r--runsc/boot/BUILD9
-rw-r--r--runsc/boot/compat.go72
-rw-r--r--runsc/boot/compat_amd64.go54
-rw-r--r--runsc/boot/compat_test.go66
28 files changed, 569 insertions, 74 deletions
diff --git a/pkg/abi/linux/ioctl.go b/pkg/abi/linux/ioctl.go
index 9afc3d1ef..191b26e4d 100644
--- a/pkg/abi/linux/ioctl.go
+++ b/pkg/abi/linux/ioctl.go
@@ -18,28 +18,59 @@ package linux
//
// These are ordered by request number (low byte).
const (
- TCGETS = 0x00005401
- TCSETS = 0x00005402
- TCSETSW = 0x00005403
- TCSETSF = 0x00005404
- TIOCSCTTY = 0x0000540e
- TIOCGPGRP = 0x0000540f
- TIOCSPGRP = 0x00005410
- TIOCOUTQ = 0x00005411
- TIOCGWINSZ = 0x00005413
- TIOCSWINSZ = 0x00005414
- TIOCINQ = 0x0000541b
- FIONREAD = TIOCINQ
- FIONBIO = 0x00005421
- TIOCGPTN = 0x80045430
- TIOCSPTLCK = 0x40045431
- FIONCLEX = 0x00005450
- FIOCLEX = 0x00005451
- FIOASYNC = 0x00005452
- FIOSETOWN = 0x00008901
- SIOCSPGRP = 0x00008902
- FIOGETOWN = 0x00008903
- SIOCGPGRP = 0x00008904
+ TCGETS = 0x00005401
+ TCSETS = 0x00005402
+ TCSETSW = 0x00005403
+ TCSETSF = 0x00005404
+ TCSBRK = 0x00005409
+ TIOCEXCL = 0x0000540c
+ TIOCNXCL = 0x0000540d
+ TIOCSCTTY = 0x0000540e
+ TIOCGPGRP = 0x0000540f
+ TIOCSPGRP = 0x00005410
+ TIOCOUTQ = 0x00005411
+ TIOCSTI = 0x00005412
+ TIOCGWINSZ = 0x00005413
+ TIOCSWINSZ = 0x00005414
+ TIOCMGET = 0x00005415
+ TIOCMBIS = 0x00005416
+ TIOCMBIC = 0x00005417
+ TIOCMSET = 0x00005418
+ TIOCINQ = 0x0000541b
+ FIONREAD = TIOCINQ
+ FIONBIO = 0x00005421
+ TIOCSETD = 0x00005423
+ TIOCNOTTY = 0x00005422
+ TIOCGETD = 0x00005424
+ TCSBRKP = 0x00005425
+ TIOCSBRK = 0x00005427
+ TIOCCBRK = 0x00005428
+ TIOCGSID = 0x00005429
+ TIOCGPTN = 0x80045430
+ TIOCSPTLCK = 0x40045431
+ TIOCGDEV = 0x80045432
+ TIOCVHANGUP = 0x00005437
+ TCFLSH = 0x0000540b
+ TIOCCONS = 0x0000541d
+ TIOCSSERIAL = 0x0000541f
+ TIOCGEXCL = 0x80045440
+ TIOCGPTPEER = 0x80045441
+ TIOCGICOUNT = 0x0000545d
+ FIONCLEX = 0x00005450
+ FIOCLEX = 0x00005451
+ FIOASYNC = 0x00005452
+ FIOSETOWN = 0x00008901
+ SIOCSPGRP = 0x00008902
+ FIOGETOWN = 0x00008903
+ SIOCGPGRP = 0x00008904
+)
+
+// ioctl(2) requests provided by uapi/linux/sockios.h
+const (
+ SIOCGIFMEM = 0x891f
+ SIOCGIFPFLAGS = 0x8935
+ SIOCGMIIPHY = 0x8947
+ SIOCGMIIREG = 0x8948
)
// ioctl(2) requests provided by uapi/linux/android/binder.h
diff --git a/pkg/abi/linux/prctl.go b/pkg/abi/linux/prctl.go
index e152c4c27..db3206f36 100644
--- a/pkg/abi/linux/prctl.go
+++ b/pkg/abi/linux/prctl.go
@@ -22,26 +22,102 @@ const (
// PR_GET_PDEATHSIG will get the process' death signal.
PR_GET_PDEATHSIG = 2
+ // PR_GET_DUMPABLE will get the process's dumpable flag.
+ PR_GET_DUMPABLE = 3
+
+ // PR_SET_DUMPABLE will set the process's dumpable flag.
+ PR_SET_DUMPABLE = 4
+
// PR_GET_KEEPCAPS will get the value of the keep capabilities flag.
PR_GET_KEEPCAPS = 7
// PR_SET_KEEPCAPS will set the value of the keep capabilities flag.
PR_SET_KEEPCAPS = 8
+ // PR_GET_TIMING will get the process's timing method.
+ PR_GET_TIMING = 13
+
+ // PR_SET_TIMING will set the process's timing method.
+ PR_SET_TIMING = 14
+
// PR_SET_NAME will set the process' name.
PR_SET_NAME = 15
// PR_GET_NAME will get the process' name.
PR_GET_NAME = 16
+ // PR_GET_SECCOMP will get a process' seccomp mode.
+ PR_GET_SECCOMP = 21
+
+ // PR_SET_SECCOMP will set a process' seccomp mode.
+ PR_SET_SECCOMP = 22
+
+ // PR_CAPBSET_READ will get the capability bounding set.
+ PR_CAPBSET_READ = 23
+
+ // PR_CAPBSET_DROP will set the capability bounding set.
+ PR_CAPBSET_DROP = 24
+
+ // PR_GET_TSC will get the the value of the flag determining whether the
+ // timestamp counter can be read.
+ PR_GET_TSC = 25
+
+ // PR_SET_TSC will set the the value of the flag determining whether the
+ // timestamp counter can be read.
+ PR_SET_TSC = 26
+
+ // PR_SET_TIMERSLACK set the process's time slack.
+ PR_SET_TIMERSLACK = 29
+
+ // PR_GET_TIMERSLACK get the process's time slack.
+ PR_GET_TIMERSLACK = 30
+
+ // PR_TASK_PERF_EVENTS_DISABLE disable all performance counters attached to
+ // the calling process.
+ PR_TASK_PERF_EVENTS_DISABLE = 31
+
+ // PR_TASK_PERF_EVENTS_ENABLE enable all performance counters attached to
+ // the calling process.
+ PR_TASK_PERF_EVENTS_ENABLE = 32
+
+ // PR_MCE_KILL set the machine check memory corruption kill policy for the
+ // calling thread.
+ PR_MCE_KILL = 33
+
+ // PR_MCE_KILL_GET get the machine check memory corruption kill policy for the
+ // calling thread.
+ PR_MCE_KILL_GET = 34
+
// PR_SET_MM will modify certain kernel memory map descriptor fields of the
// calling process. See prctl(2) for more information.
PR_SET_MM = 35
+ PR_SET_MM_START_CODE = 1
+ PR_SET_MM_END_CODE = 2
+ PR_SET_MM_START_DATA = 3
+ PR_SET_MM_END_DATA = 4
+ PR_SET_MM_START_STACK = 5
+ PR_SET_MM_START_BRK = 6
+ PR_SET_MM_BRK = 7
+ PR_SET_MM_ARG_START = 8
+ PR_SET_MM_ARG_END = 9
+ PR_SET_MM_ENV_START = 10
+ PR_SET_MM_ENV_END = 11
+ PR_SET_MM_AUXV = 12
// PR_SET_MM_EXE_FILE will supersede the /proc/pid/exe symbolic link with a
// new one pointing to a new executable file identified by the file descriptor
// provided in arg3 argument. See prctl(2) for more information.
PR_SET_MM_EXE_FILE = 13
+ PR_SET_MM_MAP = 14
+ PR_SET_MM_MAP_SIZE = 15
+
+ // PR_SET_CHILD_SUBREAPER set the "child subreaper" attribute of the calling
+ // process.
+ PR_SET_CHILD_SUBREAPER = 36
+
+ // PR_GET_CHILD_SUBREAPER get the "child subreaper" attribute of the calling
+ // process.
+ PR_GET_CHILD_SUBREAPER = 37
// PR_SET_NO_NEW_PRIVS will set the calling thread's no_new_privs bit.
PR_SET_NO_NEW_PRIVS = 38
@@ -49,17 +125,24 @@ const (
// PR_GET_NO_NEW_PRIVS will get the calling thread's no_new_privs bit.
PR_GET_NO_NEW_PRIVS = 39
- // PR_SET_SECCOMP will set a process' seccomp mode.
- PR_SET_SECCOMP = 22
+ // PR_GET_TID_ADDRESS retrieve the clear_child_tid address.
+ PR_GET_TID_ADDRESS = 40
- // PR_GET_SECCOMP will get a process' seccomp mode.
- PR_GET_SECCOMP = 21
+ // PR_SET_THP_DISABLE set the state of the "THP disable" flag for the calling
+ // thread.
+ PR_SET_THP_DISABLE = 41
- // PR_CAPBSET_READ will get the capability bounding set.
- PR_CAPBSET_READ = 23
+ // PR_GET_THP_DISABLE get the state of the "THP disable" flag for the calling
+ // thread.
+ PR_GET_THP_DISABLE = 42
- // PR_CAPBSET_DROP will set the capability bounding set.
- PR_CAPBSET_DROP = 24
+ // PR_MPX_ENABLE_MANAGEMENT enable kernel management of Memory Protection
+ // eXtensions (MPX) bounds tables.
+ PR_MPX_ENABLE_MANAGEMENT = 43
+
+ // PR_MPX_DISABLE_MANAGEMENTdisable kernel management of Memory Protection
+ // eXtensions (MPX) bounds tables.
+ PR_MPX_DISABLE_MANAGEMENT = 44
)
// From <asm/prctl.h>
diff --git a/pkg/sentry/fs/host/BUILD b/pkg/sentry/fs/host/BUILD
index 4f264a024..d1eb9bd64 100644
--- a/pkg/sentry/fs/host/BUILD
+++ b/pkg/sentry/fs/host/BUILD
@@ -43,6 +43,7 @@ go_library(
"//pkg/sentry/socket/control",
"//pkg/sentry/socket/unix",
"//pkg/sentry/socket/unix/transport",
+ "//pkg/sentry/unimpl",
"//pkg/sentry/uniqueid",
"//pkg/sentry/usermem",
"//pkg/syserr",
diff --git a/pkg/sentry/fs/host/tty.go b/pkg/sentry/fs/host/tty.go
index cf3639c46..f0bcdc908 100644
--- a/pkg/sentry/fs/host/tty.go
+++ b/pkg/sentry/fs/host/tty.go
@@ -22,6 +22,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
)
@@ -179,6 +180,35 @@ func (t *TTYFileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.
err := ioctlSetWinsize(fd, &winsize)
return 0, err
+ // Unimplemented commands.
+ case linux.TIOCSETD,
+ linux.TIOCSBRK,
+ linux.TIOCCBRK,
+ linux.TCSBRK,
+ linux.TCSBRKP,
+ linux.TIOCSTI,
+ linux.TIOCCONS,
+ linux.FIONBIO,
+ linux.TIOCEXCL,
+ linux.TIOCNXCL,
+ linux.TIOCGEXCL,
+ linux.TIOCNOTTY,
+ linux.TIOCSCTTY,
+ linux.TIOCGSID,
+ linux.TIOCGETD,
+ linux.TIOCVHANGUP,
+ linux.TIOCGDEV,
+ linux.TIOCMGET,
+ linux.TIOCMSET,
+ linux.TIOCMBIC,
+ linux.TIOCMBIS,
+ linux.TIOCGICOUNT,
+ linux.TCFLSH,
+ linux.TIOCSSERIAL,
+ linux.TIOCGPTPEER:
+
+ unimpl.EmitUnimplementedEvent(ctx)
+ fallthrough
default:
return 0, syserror.ENOTTY
}
diff --git a/pkg/sentry/fs/tty/BUILD b/pkg/sentry/fs/tty/BUILD
index d4dd20e30..2b45069a6 100644
--- a/pkg/sentry/fs/tty/BUILD
+++ b/pkg/sentry/fs/tty/BUILD
@@ -27,6 +27,7 @@ go_library(
"//pkg/sentry/kernel/auth",
"//pkg/sentry/kernel/time",
"//pkg/sentry/socket/unix/transport",
+ "//pkg/sentry/unimpl",
"//pkg/sentry/usermem",
"//pkg/syserror",
"//pkg/waiter",
diff --git a/pkg/sentry/fs/tty/master.go b/pkg/sentry/fs/tty/master.go
index dad0cad79..00bec4c2c 100644
--- a/pkg/sentry/fs/tty/master.go
+++ b/pkg/sentry/fs/tty/master.go
@@ -20,6 +20,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs/fsutil"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserror"
"gvisor.googlesource.com/gvisor/pkg/waiter"
@@ -149,7 +150,7 @@ func (mf *masterFileOperations) Write(ctx context.Context, _ *fs.File, src userm
// Ioctl implements fs.FileOperations.Ioctl.
func (mf *masterFileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
- switch args[1].Uint() {
+ switch cmd := args[1].Uint(); cmd {
case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ
// Get the number of bytes in the output queue read buffer.
return 0, mf.t.ld.outputQueueReadSize(ctx, io, args)
@@ -177,6 +178,48 @@ func (mf *masterFileOperations) Ioctl(ctx context.Context, io usermem.IO, args a
case linux.TIOCSWINSZ:
return 0, mf.t.ld.setWindowSize(ctx, io, args)
default:
+ maybeEmitUnimplementedEvent(ctx, cmd)
return 0, syserror.ENOTTY
}
}
+
+// maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid.
+func maybeEmitUnimplementedEvent(ctx context.Context, cmd uint32) {
+ switch cmd {
+ case linux.TCGETS,
+ linux.TCSETS,
+ linux.TCSETSW,
+ linux.TCSETSF,
+ linux.TIOCGPGRP,
+ linux.TIOCSPGRP,
+ linux.TIOCGWINSZ,
+ linux.TIOCSWINSZ,
+ linux.TIOCSETD,
+ linux.TIOCSBRK,
+ linux.TIOCCBRK,
+ linux.TCSBRK,
+ linux.TCSBRKP,
+ linux.TIOCSTI,
+ linux.TIOCCONS,
+ linux.FIONBIO,
+ linux.TIOCEXCL,
+ linux.TIOCNXCL,
+ linux.TIOCGEXCL,
+ linux.TIOCNOTTY,
+ linux.TIOCSCTTY,
+ linux.TIOCGSID,
+ linux.TIOCGETD,
+ linux.TIOCVHANGUP,
+ linux.TIOCGDEV,
+ linux.TIOCMGET,
+ linux.TIOCMSET,
+ linux.TIOCMBIC,
+ linux.TIOCMBIS,
+ linux.TIOCGICOUNT,
+ linux.TCFLSH,
+ linux.TIOCSSERIAL,
+ linux.TIOCGPTPEER:
+
+ unimpl.EmitUnimplementedEvent(ctx)
+ }
+}
diff --git a/pkg/sentry/fs/tty/slave.go b/pkg/sentry/fs/tty/slave.go
index 9de3168bf..a696fbb51 100644
--- a/pkg/sentry/fs/tty/slave.go
+++ b/pkg/sentry/fs/tty/slave.go
@@ -134,7 +134,7 @@ func (sf *slaveFileOperations) Write(ctx context.Context, _ *fs.File, src userme
// Ioctl implements fs.FileOperations.Ioctl.
func (sf *slaveFileOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
- switch args[1].Uint() {
+ switch cmd := args[1].Uint(); cmd {
case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ
// Get the number of bytes in the input queue read buffer.
return 0, sf.si.t.ld.inputQueueReadSize(ctx, io, args)
@@ -161,6 +161,7 @@ func (sf *slaveFileOperations) Ioctl(ctx context.Context, io usermem.IO, args ar
// control.
return 0, nil
default:
+ maybeEmitUnimplementedEvent(ctx, cmd)
return 0, syserror.ENOTTY
}
}
diff --git a/pkg/sentry/kernel/BUILD b/pkg/sentry/kernel/BUILD
index e2fb61ba6..389824b25 100644
--- a/pkg/sentry/kernel/BUILD
+++ b/pkg/sentry/kernel/BUILD
@@ -157,6 +157,8 @@ go_library(
"//pkg/sentry/socket/netlink/port",
"//pkg/sentry/socket/unix/transport",
"//pkg/sentry/time",
+ "//pkg/sentry/unimpl",
+ "//pkg/sentry/unimpl:unimplemented_syscall_go_proto",
"//pkg/sentry/uniqueid",
"//pkg/sentry/usage",
"//pkg/sentry/usermem",
diff --git a/pkg/sentry/kernel/kernel.go b/pkg/sentry/kernel/kernel.go
index bad558d48..17425e656 100644
--- a/pkg/sentry/kernel/kernel.go
+++ b/pkg/sentry/kernel/kernel.go
@@ -40,6 +40,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/cpuid"
+ "gvisor.googlesource.com/gvisor/pkg/eventchannel"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
@@ -58,6 +59,8 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/netlink/port"
sentrytime "gvisor.googlesource.com/gvisor/pkg/sentry/time"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
+ uspb "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl/unimplemented_syscall_go_proto"
"gvisor.googlesource.com/gvisor/pkg/sentry/uniqueid"
"gvisor.googlesource.com/gvisor/pkg/state"
"gvisor.googlesource.com/gvisor/pkg/tcpip"
@@ -595,6 +598,8 @@ func (ctx *createProcessContext) Value(key interface{}) interface{} {
return ctx.k
case uniqueid.CtxInotifyCookie:
return ctx.k.GenerateInotifyCookie()
+ case unimpl.CtxEvents:
+ return ctx.k
default:
return nil
}
@@ -1033,6 +1038,16 @@ func (k *Kernel) SupervisorContext() context.Context {
}
}
+// EmitUnimplementedEvent emits an UnimplementedSyscall event via the event
+// channel.
+func (k *Kernel) EmitUnimplementedEvent(ctx context.Context) {
+ t := TaskFromContext(ctx)
+ eventchannel.Emit(&uspb.UnimplementedSyscall{
+ Tid: int32(t.ThreadID()),
+ Registers: t.Arch().StateData().Proto(),
+ })
+}
+
type supervisorContext struct {
context.NoopSleeper
log.Logger
@@ -1073,6 +1088,8 @@ func (ctx supervisorContext) Value(key interface{}) interface{} {
return ctx.k
case uniqueid.CtxInotifyCookie:
return ctx.k.GenerateInotifyCookie()
+ case unimpl.CtxEvents:
+ return ctx.k
default:
return nil
}
diff --git a/pkg/sentry/kernel/pipe/reader_writer.go b/pkg/sentry/kernel/pipe/reader_writer.go
index 63efc5bbe..36be1efc3 100644
--- a/pkg/sentry/kernel/pipe/reader_writer.go
+++ b/pkg/sentry/kernel/pipe/reader_writer.go
@@ -19,6 +19,7 @@ import (
"math"
"syscall"
+ "gvisor.googlesource.com/gvisor/pkg/abi/linux"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
@@ -77,7 +78,7 @@ func (rw *ReaderWriter) Readiness(mask waiter.EventMask) waiter.EventMask {
func (rw *ReaderWriter) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) {
// Switch on ioctl request.
switch int(args[1].Int()) {
- case syscall.TIOCINQ:
+ case linux.FIONREAD:
v := rw.queuedSize()
if v > math.MaxInt32 {
panic(fmt.Sprintf("Impossibly large pipe queued size: %d", v))
diff --git a/pkg/sentry/kernel/task.go b/pkg/sentry/kernel/task.go
index e22ec768d..73ba8bee9 100644
--- a/pkg/sentry/kernel/task.go
+++ b/pkg/sentry/kernel/task.go
@@ -30,6 +30,7 @@ import (
ktime "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/time"
"gvisor.googlesource.com/gvisor/pkg/sentry/limits"
"gvisor.googlesource.com/gvisor/pkg/sentry/platform"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
"gvisor.googlesource.com/gvisor/pkg/sentry/uniqueid"
"gvisor.googlesource.com/gvisor/pkg/sentry/usage"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
@@ -594,6 +595,8 @@ func (t *Task) Value(key interface{}) interface{} {
return t.k
case uniqueid.CtxInotifyCookie:
return t.k.GenerateInotifyCookie()
+ case unimpl.CtxEvents:
+ return t.k
default:
return nil
}
diff --git a/pkg/sentry/socket/epsocket/BUILD b/pkg/sentry/socket/epsocket/BUILD
index dbabc931c..da4aaf510 100644
--- a/pkg/sentry/socket/epsocket/BUILD
+++ b/pkg/sentry/socket/epsocket/BUILD
@@ -32,6 +32,7 @@ go_library(
"//pkg/sentry/safemem",
"//pkg/sentry/socket",
"//pkg/sentry/socket/unix/transport",
+ "//pkg/sentry/unimpl",
"//pkg/sentry/usermem",
"//pkg/syserr",
"//pkg/syserror",
diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go
index e90ef4835..39a0b9941 100644
--- a/pkg/sentry/socket/epsocket/epsocket.go
+++ b/pkg/sentry/socket/epsocket/epsocket.go
@@ -45,6 +45,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/safemem"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserr"
"gvisor.googlesource.com/gvisor/pkg/syserror"
@@ -1184,6 +1185,8 @@ func Ioctl(ctx context.Context, ep commonEndpoint, io usermem.IO, args arch.Sysc
})
return 0, err
+ case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG:
+ unimpl.EmitUnimplementedEvent(ctx)
}
return 0, syserror.ENOTTY
diff --git a/pkg/sentry/socket/rpcinet/BUILD b/pkg/sentry/socket/rpcinet/BUILD
index 3ea433360..38fa54283 100644
--- a/pkg/sentry/socket/rpcinet/BUILD
+++ b/pkg/sentry/socket/rpcinet/BUILD
@@ -32,6 +32,7 @@ go_library(
"//pkg/sentry/socket/rpcinet/conn",
"//pkg/sentry/socket/rpcinet/notifier",
"//pkg/sentry/socket/unix/transport",
+ "//pkg/sentry/unimpl",
"//pkg/sentry/usermem",
"//pkg/syserr",
"//pkg/syserror",
diff --git a/pkg/sentry/socket/rpcinet/socket.go b/pkg/sentry/socket/rpcinet/socket.go
index 44fa5c620..788d853c9 100644
--- a/pkg/sentry/socket/rpcinet/socket.go
+++ b/pkg/sentry/socket/rpcinet/socket.go
@@ -32,6 +32,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/notifier"
pb "gvisor.googlesource.com/gvisor/pkg/sentry/socket/rpcinet/syscall_rpc_go_proto"
"gvisor.googlesource.com/gvisor/pkg/sentry/socket/unix/transport"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl"
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
"gvisor.googlesource.com/gvisor/pkg/syserr"
"gvisor.googlesource.com/gvisor/pkg/syserror"
@@ -555,6 +556,10 @@ func (s *socketOperations) Ioctl(ctx context.Context, io usermem.IO, args arch.S
})
return 0, err
+
+ case linux.SIOCGIFMEM, linux.SIOCGIFPFLAGS, linux.SIOCGMIIPHY, linux.SIOCGMIIREG:
+ unimpl.EmitUnimplementedEvent(ctx)
+
default:
return 0, syserror.ENOTTY
}
diff --git a/pkg/sentry/syscalls/BUILD b/pkg/sentry/syscalls/BUILD
index 22a757095..2a9f0915e 100644
--- a/pkg/sentry/syscalls/BUILD
+++ b/pkg/sentry/syscalls/BUILD
@@ -1,7 +1,6 @@
package(licenses = ["notice"]) # Apache 2.0
load("//tools/go_stateify:defs.bzl", "go_library")
-load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
go_library(
name = "syscalls",
@@ -13,9 +12,7 @@ go_library(
importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls",
visibility = ["//:sandbox"],
deps = [
- ":unimplemented_syscall_go_proto",
"//pkg/abi/linux",
- "//pkg/eventchannel",
"//pkg/sentry/arch",
"//pkg/sentry/fs",
"//pkg/sentry/kernel",
@@ -26,18 +23,3 @@ go_library(
"//pkg/waiter",
],
)
-
-proto_library(
- name = "unimplemented_syscall_proto",
- srcs = ["unimplemented_syscall.proto"],
- visibility = ["//visibility:public"],
- deps = ["//pkg/sentry/arch:registers_proto"],
-)
-
-go_proto_library(
- name = "unimplemented_syscall_go_proto",
- importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/unimplemented_syscall_go_proto",
- proto = ":unimplemented_syscall_proto",
- visibility = ["//visibility:public"],
- deps = ["//pkg/sentry/arch:registers_go_proto"],
-)
diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go
index 75e87f5ec..11bf81f88 100644
--- a/pkg/sentry/syscalls/linux/linux64.go
+++ b/pkg/sentry/syscalls/linux/linux64.go
@@ -369,7 +369,7 @@ var AMD64 = &kernel.SyscallTable{
0xffffffffff600800: 309, // vsyscall getcpu(2)
},
Missing: func(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, error) {
- syscalls.UnimplementedEvent(t)
+ t.Kernel().EmitUnimplementedEvent(t)
return 0, syserror.ENOSYS
},
}
diff --git a/pkg/sentry/syscalls/linux/sys_prctl.go b/pkg/sentry/syscalls/linux/sys_prctl.go
index c7b39ede8..91e852049 100644
--- a/pkg/sentry/syscalls/linux/sys_prctl.go
+++ b/pkg/sentry/syscalls/linux/sys_prctl.go
@@ -104,6 +104,22 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
// Set the underlying executable.
t.MemoryManager().SetExecutable(file.Dirent)
+
+ case linux.PR_SET_MM_AUXV,
+ linux.PR_SET_MM_START_CODE,
+ linux.PR_SET_MM_END_CODE,
+ linux.PR_SET_MM_START_DATA,
+ linux.PR_SET_MM_END_DATA,
+ linux.PR_SET_MM_START_STACK,
+ linux.PR_SET_MM_START_BRK,
+ linux.PR_SET_MM_BRK,
+ linux.PR_SET_MM_ARG_START,
+ linux.PR_SET_MM_ARG_END,
+ linux.PR_SET_MM_ENV_START,
+ linux.PR_SET_MM_ENV_END:
+
+ t.Kernel().EmitUnimplementedEvent(t)
+ fallthrough
default:
return 0, nil, syscall.EINVAL
}
@@ -151,8 +167,29 @@ func Prctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
}
return 0, nil, t.DropBoundingCapability(cp)
+ case linux.PR_GET_DUMPABLE,
+ linux.PR_SET_DUMPABLE,
+ linux.PR_GET_TIMING,
+ linux.PR_SET_TIMING,
+ linux.PR_GET_TSC,
+ linux.PR_SET_TSC,
+ linux.PR_TASK_PERF_EVENTS_DISABLE,
+ linux.PR_TASK_PERF_EVENTS_ENABLE,
+ linux.PR_GET_TIMERSLACK,
+ linux.PR_SET_TIMERSLACK,
+ linux.PR_MCE_KILL,
+ linux.PR_MCE_KILL_GET,
+ linux.PR_GET_TID_ADDRESS,
+ linux.PR_SET_CHILD_SUBREAPER,
+ linux.PR_GET_CHILD_SUBREAPER,
+ linux.PR_GET_THP_DISABLE,
+ linux.PR_SET_THP_DISABLE,
+ linux.PR_MPX_ENABLE_MANAGEMENT,
+ linux.PR_MPX_DISABLE_MANAGEMENT:
+
+ t.Kernel().EmitUnimplementedEvent(t)
+ fallthrough
default:
- t.Warningf("Unsupported prctl %d", option)
return 0, nil, syscall.EINVAL
}
diff --git a/pkg/sentry/syscalls/linux/sys_shm.go b/pkg/sentry/syscalls/linux/sys_shm.go
index b13d48b98..5f887523a 100644
--- a/pkg/sentry/syscalls/linux/sys_shm.go
+++ b/pkg/sentry/syscalls/linux/sys_shm.go
@@ -147,6 +147,7 @@ func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
// We currently do not support memmory locking anywhere.
// mlock(2)/munlock(2) are currently stubbed out as no-ops so do the
// same here.
+ t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, nil
default:
diff --git a/pkg/sentry/syscalls/linux/sys_tls.go b/pkg/sentry/syscalls/linux/sys_tls.go
index 27ddb3808..40e84825b 100644
--- a/pkg/sentry/syscalls/linux/sys_tls.go
+++ b/pkg/sentry/syscalls/linux/sys_tls.go
@@ -45,6 +45,9 @@ func ArchPrctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sys
regs.Fs = 0
regs.Fs_base = fsbase
+ case linux.ARCH_GET_GS, linux.ARCH_SET_GS:
+ t.Kernel().EmitUnimplementedEvent(t)
+ fallthrough
default:
return 0, nil, syscall.EINVAL
}
diff --git a/pkg/sentry/syscalls/syscalls.go b/pkg/sentry/syscalls/syscalls.go
index bae32d727..425ce900c 100644
--- a/pkg/sentry/syscalls/syscalls.go
+++ b/pkg/sentry/syscalls/syscalls.go
@@ -26,10 +26,8 @@ package syscalls
import (
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
- "gvisor.googlesource.com/gvisor/pkg/eventchannel"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
- uspb "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/unimplemented_syscall_go_proto"
"gvisor.googlesource.com/gvisor/pkg/syserror"
)
@@ -44,7 +42,7 @@ func Error(err error) kernel.SyscallFn {
// syscall event via the event channel and returns the passed error.
func ErrorWithEvent(err error) kernel.SyscallFn {
return func(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
- UnimplementedEvent(t)
+ t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, err
}
}
@@ -57,16 +55,7 @@ func CapError(c linux.Capability) kernel.SyscallFn {
if !t.HasCapability(c) {
return 0, nil, syserror.EPERM
}
- UnimplementedEvent(t)
+ t.Kernel().EmitUnimplementedEvent(t)
return 0, nil, syserror.ENOSYS
}
}
-
-// UnimplementedEvent emits an UnimplementedSyscall event via the event
-// channel.
-func UnimplementedEvent(t *kernel.Task) {
- eventchannel.Emit(&uspb.UnimplementedSyscall{
- Tid: int32(t.ThreadID()),
- Registers: t.Arch().StateData().Proto(),
- })
-}
diff --git a/pkg/sentry/unimpl/BUILD b/pkg/sentry/unimpl/BUILD
new file mode 100644
index 000000000..63da5e81f
--- /dev/null
+++ b/pkg/sentry/unimpl/BUILD
@@ -0,0 +1,30 @@
+package(licenses = ["notice"]) # Apache 2.0
+
+load("//tools/go_stateify:defs.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+
+proto_library(
+ name = "unimplemented_syscall_proto",
+ srcs = ["unimplemented_syscall.proto"],
+ visibility = ["//visibility:public"],
+ deps = ["//pkg/sentry/arch:registers_proto"],
+)
+
+go_proto_library(
+ name = "unimplemented_syscall_go_proto",
+ importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl/unimplemented_syscall_go_proto",
+ proto = ":unimplemented_syscall_proto",
+ visibility = ["//visibility:public"],
+ deps = ["//pkg/sentry/arch:registers_go_proto"],
+)
+
+go_library(
+ name = "unimpl",
+ srcs = ["events.go"],
+ importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl",
+ visibility = ["//:sandbox"],
+ deps = [
+ "//pkg/log",
+ "//pkg/sentry/context",
+ ],
+)
diff --git a/pkg/sentry/unimpl/events.go b/pkg/sentry/unimpl/events.go
new file mode 100644
index 000000000..f78f8c981
--- /dev/null
+++ b/pkg/sentry/unimpl/events.go
@@ -0,0 +1,45 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package unimpl contains interface to emit events about unimplemented
+// features.
+package unimpl
+
+import (
+ "gvisor.googlesource.com/gvisor/pkg/log"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/context"
+)
+
+// contextID is the events package's type for context.Context.Value keys.
+type contextID int
+
+const (
+ // CtxEvents is a Context.Value key for a Events.
+ CtxEvents contextID = iota
+)
+
+// Events interface defines method to emit unsupported events.
+type Events interface {
+ EmitUnimplementedEvent(context.Context)
+}
+
+// EmitUnimplementedEvent emits unsupported syscall event to the context.
+func EmitUnimplementedEvent(ctx context.Context) {
+ e := ctx.Value(CtxEvents)
+ if e == nil {
+ log.Warningf("Context.Value(CtxEvents) not present, unimplemented syscall event not reported.")
+ return
+ }
+ e.(Events).EmitUnimplementedEvent(ctx)
+}
diff --git a/pkg/sentry/syscalls/unimplemented_syscall.proto b/pkg/sentry/unimpl/unimplemented_syscall.proto
index 41579b016..41579b016 100644
--- a/pkg/sentry/syscalls/unimplemented_syscall.proto
+++ b/pkg/sentry/unimpl/unimplemented_syscall.proto
diff --git a/runsc/boot/BUILD b/runsc/boot/BUILD
index f8f848ebf..04cc0e854 100644
--- a/runsc/boot/BUILD
+++ b/runsc/boot/BUILD
@@ -6,6 +6,7 @@ go_library(
name = "boot",
srcs = [
"compat.go",
+ "compat_amd64.go",
"config.go",
"controller.go",
"debug.go",
@@ -59,9 +60,9 @@ go_library(
"//pkg/sentry/socket/unix",
"//pkg/sentry/state",
"//pkg/sentry/strace",
- "//pkg/sentry/syscalls:unimplemented_syscall_go_proto",
"//pkg/sentry/syscalls/linux",
"//pkg/sentry/time",
+ "//pkg/sentry/unimpl:unimplemented_syscall_go_proto",
"//pkg/sentry/usage",
"//pkg/sentry/watchdog",
"//pkg/syserror",
@@ -87,12 +88,16 @@ go_library(
go_test(
name = "boot_test",
size = "small",
- srcs = ["loader_test.go"],
+ srcs = [
+ "compat_test.go",
+ "loader_test.go",
+ ],
embed = [":boot"],
deps = [
"//pkg/control/server",
"//pkg/log",
"//pkg/p9",
+ "//pkg/sentry/arch:registers_go_proto",
"//pkg/sentry/context/contexttest",
"//pkg/sentry/fs",
"//pkg/unet",
diff --git a/runsc/boot/compat.go b/runsc/boot/compat.go
index 6766953b3..d18c2f802 100644
--- a/runsc/boot/compat.go
+++ b/runsc/boot/compat.go
@@ -17,6 +17,8 @@ package boot
import (
"fmt"
"os"
+ "sync"
+ "syscall"
"github.com/golang/protobuf/proto"
"gvisor.googlesource.com/gvisor/pkg/abi"
@@ -25,7 +27,7 @@ import (
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto"
"gvisor.googlesource.com/gvisor/pkg/sentry/strace"
- spb "gvisor.googlesource.com/gvisor/pkg/sentry/syscalls/unimplemented_syscall_go_proto"
+ spb "gvisor.googlesource.com/gvisor/pkg/sentry/unimpl/unimplemented_syscall_go_proto"
)
func initCompatLogs(fd int) error {
@@ -40,15 +42,27 @@ func initCompatLogs(fd int) error {
type compatEmitter struct {
sink *log.BasicLogger
nameMap strace.SyscallMap
+
+ // mu protects the fields below.
+ mu sync.Mutex
+
+ // trackers map syscall number to the respective tracker instance.
+ // Protected by 'mu'.
+ trackers map[uint64]syscallTracker
}
func newCompatEmitter(logFD int) (*compatEmitter, error) {
- // Always logs to default logger.
nameMap, ok := strace.Lookup(abi.Linux, arch.AMD64)
if !ok {
return nil, fmt.Errorf("amd64 Linux syscall table not found")
}
- c := &compatEmitter{sink: log.Log(), nameMap: nameMap}
+
+ c := &compatEmitter{
+ // Always logs to default logger.
+ sink: log.Log(),
+ nameMap: nameMap,
+ trackers: make(map[uint64]syscallTracker),
+ }
if logFD > 0 {
f := os.NewFile(uintptr(logFD), "user log file")
@@ -61,10 +75,33 @@ func newCompatEmitter(logFD int) (*compatEmitter, error) {
// Emit implements eventchannel.Emitter.
func (c *compatEmitter) Emit(msg proto.Message) (hangup bool, err error) {
// Only interested in UnimplementedSyscall, skip the rest.
- if us, ok := msg.(*spb.UnimplementedSyscall); ok {
- regs := us.Registers.GetArch().(*rpb.Registers_Amd64).Amd64
- sysnr := regs.OrigRax
+ us, ok := msg.(*spb.UnimplementedSyscall)
+ if !ok {
+ return false, nil
+ }
+ regs := us.Registers.GetArch().(*rpb.Registers_Amd64).Amd64
+
+ c.mu.Lock()
+ defer c.mu.Unlock()
+
+ sysnr := regs.OrigRax
+ tr := c.trackers[sysnr]
+ if tr == nil {
+ switch sysnr {
+ case syscall.SYS_PRCTL, syscall.SYS_ARCH_PRCTL:
+ tr = newCmdTracker(0)
+
+ case syscall.SYS_IOCTL, syscall.SYS_EPOLL_CTL, syscall.SYS_SHMCTL:
+ tr = newCmdTracker(1)
+
+ default:
+ tr = &onceTracker{}
+ }
+ c.trackers[sysnr] = tr
+ }
+ if tr.shouldReport(regs) {
c.sink.Infof("Unsupported syscall: %s, regs: %+v", c.nameMap.Name(uintptr(sysnr)), regs)
+ tr.onReported(regs)
}
return false, nil
}
@@ -74,3 +111,26 @@ func (c *compatEmitter) Close() error {
c.sink = nil
return nil
}
+
+// syscallTracker interface allows filters to apply differently depending on
+// the syscall and arguments.
+type syscallTracker interface {
+ // shouldReport returns true is the syscall should be reported.
+ shouldReport(regs *rpb.AMD64Registers) bool
+
+ // onReported marks the syscall as reported.
+ onReported(regs *rpb.AMD64Registers)
+}
+
+// onceTracker reports only a single time, used for most syscalls.
+type onceTracker struct {
+ reported bool
+}
+
+func (o *onceTracker) shouldReport(_ *rpb.AMD64Registers) bool {
+ return !o.reported
+}
+
+func (o *onceTracker) onReported(_ *rpb.AMD64Registers) {
+ o.reported = true
+}
diff --git a/runsc/boot/compat_amd64.go b/runsc/boot/compat_amd64.go
new file mode 100644
index 000000000..2bb769a49
--- /dev/null
+++ b/runsc/boot/compat_amd64.go
@@ -0,0 +1,54 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package boot
+
+import (
+ "fmt"
+
+ rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto"
+)
+
+// cmdTracker reports only a single time for each different command argument in
+// the syscall. It's used for generic syscalls like ioctl to report once per
+// 'cmd'
+type cmdTracker struct {
+ // argIdx is the syscall argument index where the command is located.
+ argIdx int
+ cmds map[uint32]struct{}
+}
+
+func newCmdTracker(argIdx int) *cmdTracker {
+ return &cmdTracker{argIdx: argIdx, cmds: make(map[uint32]struct{})}
+}
+
+// cmd returns the command based on the syscall argument index.
+func (c *cmdTracker) cmd(regs *rpb.AMD64Registers) uint32 {
+ switch c.argIdx {
+ case 0:
+ return uint32(regs.Rdi)
+ case 1:
+ return uint32(regs.Rsi)
+ }
+ panic(fmt.Sprintf("unsupported syscall argument index %d", c.argIdx))
+}
+
+func (c *cmdTracker) shouldReport(regs *rpb.AMD64Registers) bool {
+ _, ok := c.cmds[c.cmd(regs)]
+ return !ok
+}
+
+func (c *cmdTracker) onReported(regs *rpb.AMD64Registers) {
+ c.cmds[c.cmd(regs)] = struct{}{}
+}
diff --git a/runsc/boot/compat_test.go b/runsc/boot/compat_test.go
new file mode 100644
index 000000000..30b94798a
--- /dev/null
+++ b/runsc/boot/compat_test.go
@@ -0,0 +1,66 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package boot
+
+import (
+ "testing"
+
+ rpb "gvisor.googlesource.com/gvisor/pkg/sentry/arch/registers_go_proto"
+)
+
+func TestOnceTracker(t *testing.T) {
+ o := onceTracker{}
+ if !o.shouldReport(nil) {
+ t.Error("first call to checkAndMark, got: false, want: true")
+ }
+ o.onReported(nil)
+ for i := 0; i < 2; i++ {
+ if o.shouldReport(nil) {
+ t.Error("after first call to checkAndMark, got: true, want: false")
+ }
+ }
+}
+
+func TestCmdTracker(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ idx int
+ rdi1 uint64
+ rdi2 uint64
+ rsi1 uint64
+ rsi2 uint64
+ want bool
+ }{
+ {name: "same rdi", idx: 0, rdi1: 123, rdi2: 123, want: false},
+ {name: "same rsi", idx: 1, rsi1: 123, rsi2: 123, want: false},
+ {name: "diff rdi", idx: 0, rdi1: 123, rdi2: 321, want: true},
+ {name: "diff rsi", idx: 1, rsi1: 123, rsi2: 321, want: true},
+ {name: "cmd is uint32", idx: 0, rsi1: 0xdead00000123, rsi2: 0xbeef00000123, want: false},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ c := newCmdTracker(tc.idx)
+ regs := &rpb.AMD64Registers{Rdi: tc.rdi1, Rsi: tc.rsi1}
+ if !c.shouldReport(regs) {
+ t.Error("first call to checkAndMark, got: false, want: true")
+ }
+ c.onReported(regs)
+
+ regs.Rdi, regs.Rsi = tc.rdi2, tc.rsi2
+ if got := c.shouldReport(regs); tc.want != got {
+ t.Errorf("after first call to checkAndMark, got: %t, want: %t", got, tc.want)
+ }
+ })
+ }
+}