Skip to content

Commit

Permalink
Handle select() calls that update status for fds beyond FD_SETSIZE
Browse files Browse the repository at this point in the history
This also makes smaller select()s slightly more efficient.

Resolves #3612
  • Loading branch information
rocallahan committed Sep 25, 2023
1 parent 79dbdd5 commit 9232147
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 14 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -911,6 +911,7 @@ set(BASIC_TESTS
bad_syscall
barrier
big_buffers
big_select
block
block_open
bpf
Expand Down
41 changes: 27 additions & 14 deletions src/record_syscall.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3628,6 +3628,16 @@ static Switchable did_emulate_read(int syscallno, RecordTask* t,
return PREVENT_SWITCH;
}

static ParamSize select_param_size(intptr_t nfds, SupportedArch arch) {
nfds = (int)nfds;
size_t size = 0;
if (nfds > 0) {
size_t long_size = word_size(arch);
size = ((nfds + long_size*8 - 1)/(long_size*8))*long_size;
}
return ParamSize(size);
}

template <typename Arch>
static Switchable rec_prepare_syscall_arch(RecordTask* t,
TaskSyscallState& syscall_state,
Expand Down Expand Up @@ -4027,32 +4037,35 @@ static Switchable rec_prepare_syscall_arch(RecordTask* t,
return prepare_socketcall<Arch>(t, syscall_state);

case Arch::select:
case Arch::_newselect:
case Arch::_newselect: {
ParamSize size = select_param_size(regs.arg1_signed(), Arch::arch());
if (syscallno == Arch::select &&
Arch::select_semantics == Arch::SelectStructArguments) {
auto argsp =
syscall_state.reg_parameter<typename Arch::select_args>(1, IN);
syscall_state.mem_ptr_parameter_inferred(
REMOTE_PTR_FIELD(argsp, read_fds), IN_OUT);
syscall_state.mem_ptr_parameter_inferred(
REMOTE_PTR_FIELD(argsp, write_fds), IN_OUT);
syscall_state.mem_ptr_parameter_inferred(
REMOTE_PTR_FIELD(argsp, except_fds), IN_OUT);
syscall_state.mem_ptr_parameter_inferred(
syscall_state.mem_ptr_parameter(
REMOTE_PTR_FIELD(argsp, read_fds), size, IN_OUT);
syscall_state.mem_ptr_parameter(
REMOTE_PTR_FIELD(argsp, write_fds), size, IN_OUT);
syscall_state.mem_ptr_parameter(
REMOTE_PTR_FIELD(argsp, except_fds), size, IN_OUT);
syscall_state.mem_ptr_parameter(
REMOTE_PTR_FIELD(argsp, timeout), IN_OUT);
} else {
syscall_state.reg_parameter<typename Arch::fd_set>(2, IN_OUT);
syscall_state.reg_parameter<typename Arch::fd_set>(3, IN_OUT);
syscall_state.reg_parameter<typename Arch::fd_set>(4, IN_OUT);
syscall_state.reg_parameter(2, size, IN_OUT);
syscall_state.reg_parameter(3, size, IN_OUT);
syscall_state.reg_parameter(4, size, IN_OUT);
syscall_state.reg_parameter<typename Arch::timeval>(5, IN_OUT);
}
return ALLOW_SWITCH;
}

case Arch::pselect6_time64:
case Arch::pselect6: {
syscall_state.reg_parameter<typename Arch::fd_set>(2, IN_OUT);
syscall_state.reg_parameter<typename Arch::fd_set>(3, IN_OUT);
syscall_state.reg_parameter<typename Arch::fd_set>(4, IN_OUT);
ParamSize size = select_param_size(regs.arg1_signed(), Arch::arch());
syscall_state.reg_parameter(2, size, IN_OUT);
syscall_state.reg_parameter(3, size, IN_OUT);
syscall_state.reg_parameter(4, size, IN_OUT);
if (syscallno == Arch::pselect6) {
syscall_state.reg_parameter<typename Arch::timespec>(5, IN_OUT);
} else {
Expand Down
34 changes: 34 additions & 0 deletions src/test/big_select.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */

#include "util.h"

#define NUM_LONGS (FD_SETSIZE/(8 * sizeof(long)) + 1)

int main(void) {
struct {
long longs[NUM_LONGS];
} *fdset;
int ret;
struct timeval timeout = { 0, 0 };
struct rlimit rlim = { FD_SETSIZE + 1, FD_SETSIZE + 1 };
if (setrlimit(RLIMIT_NOFILE, &rlim)) {
atomic_puts("Can't set necessary rlimit, skipping test");
atomic_puts("EXIT-SUCCESS");
return 0;
}

ret = dup2(0, FD_SETSIZE);
test_assert(ret == FD_SETSIZE);

ALLOCATE_GUARD(fdset, 'a');
memset(fdset->longs, 0, sizeof(fdset->longs));
fdset->longs[NUM_LONGS - 1] = -1;
ret = select(FD_SETSIZE + 1, (fd_set*)fdset, NULL, NULL, &timeout);
test_assert(ret == 0);
VERIFY_GUARD(fdset);

test_assert(fdset->longs[NUM_LONGS - 1] == 0);

atomic_puts("EXIT-SUCCESS");
return 0;
}

1 comment on commit 9232147

@Keno
Copy link
Member

@Keno Keno commented on 9232147 Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block-32 failed on CI with an EINVAL from select, which seems likely related. FYI.

Please sign in to comment.