Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/seccheck/server.cc
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ std::vector<Callback> dispatchers = {
unpackSyscall<::gvisor::syscall::InotifyRmWatch>,
unpackSyscall<::gvisor::syscall::SocketPair>,
unpackSyscall<::gvisor::syscall::Write>,
unpackSyscall<::gvisor::syscall::Select>,
};

void unpack(absl::string_view buf) {
Expand Down
2 changes: 2 additions & 0 deletions pkg/sentry/seccheck/metadata_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func archInit() {
},
})
addSyscallPoint(22, "pipe", nil)
addSyscallPoint(23, "select", nil)
addSyscallPoint(32, "dup", []FieldDesc{
{
ID: FieldSyscallPath,
Expand Down Expand Up @@ -149,6 +150,7 @@ func archInit() {
Name: "fd_path",
},
})
addSyscallPoint(270, "pselect6", nil)
addSyscallPoint(282, "signalfd", []FieldDesc{
{
ID: FieldSyscallPath,
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/seccheck/metadata_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ func archInit() {
Name: "fd_path",
},
})
addSyscallPoint(72, "pselect6", nil)
addSyscallPoint(74, "signalfd4", []FieldDesc{
{
ID: FieldSyscallPath,
Expand Down
1 change: 1 addition & 0 deletions pkg/sentry/seccheck/points/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,6 @@ enum MessageType {
MESSAGE_SYSCALL_INOTIFY_RM_WATCH = 32;
MESSAGE_SYSCALL_SOCKETPAIR = 33;
MESSAGE_SYSCALL_WRITE = 34;
MESSAGE_SYSCALL_SELECT = 35;
}
// LINT.ThenChange(../../../../examples/seccheck/server.cc)
11 changes: 11 additions & 0 deletions pkg/sentry/seccheck/points/syscall.proto
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,14 @@ message SocketPair {
int32 socket1 = 7;
int32 socket2 = 8;
}

message Select {
gvisor.common.ContextData context_data = 1;
Exit exit = 2;
uint64 sysno = 3;
repeated int64 read_fds = 4;
int64 nfds = 5;
int64 timeout_ms = 6;
int64 write_nfds = 7;
int64 except_nfds = 8;
}
6 changes: 3 additions & 3 deletions pkg/sentry/syscalls/linux/linux64.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ var AMD64 = &kernel.SyscallTable{
20: syscalls.SupportedPoint("writev", Writev, PointWritev),
21: syscalls.Supported("access", Access),
22: syscalls.SupportedPoint("pipe", Pipe, PointPipe),
23: syscalls.Supported("select", Select),
23: syscalls.SupportedPoint("select", Select, PointSelect),
24: syscalls.Supported("sched_yield", SchedYield),
25: syscalls.Supported("mremap", Mremap),
26: syscalls.PartiallySupported("msync", Msync, "Full data flush is not guaranteed at this time.", nil),
Expand Down Expand Up @@ -312,7 +312,7 @@ var AMD64 = &kernel.SyscallTable{
267: syscalls.Supported("readlinkat", Readlinkat),
268: syscalls.Supported("fchmodat", Fchmodat),
269: syscalls.Supported("faccessat", Faccessat),
270: syscalls.Supported("pselect6", Pselect6),
270: syscalls.SupportedPoint("pselect6", Pselect6, PointPselect6),
271: syscalls.Supported("ppoll", Ppoll),
272: syscalls.PartiallySupported("unshare", Unshare, "Time, cgroup namespaces not supported.", nil),
273: syscalls.Supported("set_robust_list", SetRobustList),
Expand Down Expand Up @@ -494,7 +494,7 @@ var ARM64 = &kernel.SyscallTable{
69: syscalls.SupportedPoint("preadv", Preadv, PointPreadv),
70: syscalls.SupportedPoint("pwritev", Pwritev, PointPwritev),
71: syscalls.Supported("sendfile", Sendfile),
72: syscalls.Supported("pselect6", Pselect6),
72: syscalls.SupportedPoint("pselect6", Pselect6, PointPselect6),
73: syscalls.Supported("ppoll", Ppoll),
74: syscalls.SupportedPoint("signalfd4", Signalfd4, PointSignalfd4),
75: syscalls.ErrorWithEvent("vmsplice", linuxerr.ENOSYS, "", []string{"gvisor.dev/issue/138"}), // TODO(b/29354098)
Expand Down
108 changes: 108 additions & 0 deletions pkg/sentry/syscalls/linux/points.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package linux

import (
"fmt"
"math/bits"

"google.golang.org/protobuf/proto"
"gvisor.dev/gvisor/pkg/abi/linux"
Expand Down Expand Up @@ -992,3 +993,110 @@ func PointSocketpair(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.Conte
p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_SOCKETPAIR
}

// populateSelectFields extracts the file descriptors from the read FD set into p.ReadFds
// (specifically to enable host auditing of guest stdin / FD 0 read selections) while
// computing the total set bit counts for write and except sets using bits.OnesCount8
// (to avoid unnecessary serialization overhead and message bloat for write/except sets).
func populateSelectFields(t *kernel.Task, nfds int, readFDs, writeFDs, exceptFDs hostarch.Addr, p *pb.Select) {
if nfds > 0 && nfds <= fileCap {
nBytes := (nfds + 7) / 8
nBitsInLastPartialByte := nfds % 8

if readFDs != 0 {
if r, err := CopyInFDSet(t, readFDs, nBytes, nBitsInLastPartialByte); err == nil {
var fd int64
for i := 0; i < nBytes; i++ {
rV := r[i]
m := byte(1)
for j := 0; j < 8; j++ {
if (rV&m) != 0 && fd < int64(nfds) {
p.ReadFds = append(p.ReadFds, fd)
}
fd++
m <<= 1
}
}
}
}

if writeFDs != 0 {
if w, err := CopyInFDSet(t, writeFDs, nBytes, nBitsInLastPartialByte); err == nil {
for i := 0; i < nBytes; i++ {
p.WriteNfds += int64(bits.OnesCount8(w[i]))
}
}
}

if exceptFDs != 0 {
if e, err := CopyInFDSet(t, exceptFDs, nBytes, nBitsInLastPartialByte); err == nil {
for i := 0; i < nBytes; i++ {
p.ExceptNfds += int64(bits.OnesCount8(e[i]))
}
}
}
}
}

// PointSelect converts select(2) syscall to proto.
// Only the read FD set is extracted as a list (to allow host auditing of stdin read selections),
// while write and except sets only report total counts to minimize message serialization overhead.
func PointSelect(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
nfds := int(info.Args[0].Int())
readFDs := info.Args[1].Pointer()
writeFDs := info.Args[2].Pointer()
exceptFDs := info.Args[3].Pointer()
timevalAddr := info.Args[4].Pointer()

p := &pb.Select{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Nfds: int64(nfds),
}

timeoutMs := int64(-1)
if timevalAddr != 0 {
var timeval linux.Timeval
if _, err := timeval.CopyIn(t, timevalAddr); err == nil {
timeoutMs = timeval.ToNsecCapped() / 1e6
}
}
p.TimeoutMs = timeoutMs

populateSelectFields(t, nfds, readFDs, writeFDs, exceptFDs, p)

p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_SELECT
}

// PointPselect6 converts pselect6(2) syscall to proto.
// Only the read FD set is extracted as a list (to allow host auditing of stdin read selections),
// while write and except sets only report total counts to minimize message serialization overhead.
func PointPselect6(t *kernel.Task, fields seccheck.FieldSet, cxtData *pb.ContextData, info kernel.SyscallInfo) (proto.Message, pb.MessageType) {
nfds := int(info.Args[0].Int())
readFDs := info.Args[1].Pointer()
writeFDs := info.Args[2].Pointer()
exceptFDs := info.Args[3].Pointer()
timespecAddr := info.Args[4].Pointer()

p := &pb.Select{
ContextData: cxtData,
Sysno: uint64(info.Sysno),
Nfds: int64(nfds),
}

timeoutMs := int64(-1)
if timespecAddr != 0 {
if timeout, err := copyTimespecInToDuration(t, timespecAddr); err == nil {
if timeout >= 0 {
timeoutMs = timeout.Nanoseconds() / 1e6
}
}
}
p.TimeoutMs = timeoutMs

populateSelectFields(t, nfds, readFDs, writeFDs, exceptFDs, p)

p.Exit = newExitMaybe(info)
return p, pb.MessageType_MESSAGE_SYSCALL_SELECT
}
9 changes: 9 additions & 0 deletions test/trace/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ func matchPoints(t *testing.T, msgs []test.Message) map[pb.MessageType]*checkers
pb.MessageType_MESSAGE_SYSCALL_INOTIFY_ADD_WATCH: {checker: checkSyscallInotifyInitAddWatch},
pb.MessageType_MESSAGE_SYSCALL_INOTIFY_RM_WATCH: {checker: checkSyscallInotifyInitRmWatch},
pb.MessageType_MESSAGE_SYSCALL_CLONE: {checker: checkSyscallClone},
pb.MessageType_MESSAGE_SYSCALL_SELECT: {checker: checkSyscallSelect},
}
return matchers
}
Expand Down Expand Up @@ -881,3 +882,11 @@ func checkSyscallInotifyInitRmWatch(msg test.Message) error {
}
return nil
}

func checkSyscallSelect(msg test.Message) error {
p := pb.Select{}
if err := proto.Unmarshal(msg.Msg, &p); err != nil {
return err
}
return checkContextData(p.ContextData)
}
11 changes: 11 additions & 0 deletions test/trace/workload/workload.cc
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,16 @@ void runInotifyRmWatch() {
rmdir(pathname);
}

void runSelect() {
timeval tv = {};
fd_set rfds;
FD_ZERO(&rfds);
int res = select(0, &rfds, nullptr, nullptr, &tv);
if (res < 0) {
err(1, "select");
}
}

} // namespace testing
} // namespace gvisor

Expand Down Expand Up @@ -697,6 +707,7 @@ int main(int argc, char** argv) {
::gvisor::testing::runInotifyInit1();
::gvisor::testing::runInotifyAddWatch();
::gvisor::testing::runInotifyRmWatch();
::gvisor::testing::runSelect();
// signalfd(2), fork(2), and vfork(2) system calls are not supported in arm
// architecture.
#ifdef __x86_64__
Expand Down
Loading