Skip to content

Commit ba2c2a2

Browse files
committed
Discard prepared-syscall state when a syscall is interrupted by tracee seccomp trap
1 parent bfe45b4 commit ba2c2a2

4 files changed

Lines changed: 57 additions & 1 deletion

File tree

src/RecordSession.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ static void handle_seccomp_trap(RecordTask* t,
276276
// PTRACE_SYSCALL to enter the syscall during handle_desched_event. Cancel
277277
// that event now since the seccomp SIGSYS aborts it completely.
278278
ASSERT(t, t->ev().Syscall().number == syscallno);
279+
// Make sure any prepared syscall state is discarded and any temporary
280+
// effects (e.g. redirecting pointers to scratch) undone.
281+
rec_abort_prepared_syscall(t);
279282
if (t->ev().type() == EV_SYSCALL_INTERRUPTION) {
280283
// The event could be a syscall-interruption if it was pushed by
281284
// `handle_desched_event`. In that case, it has not been recorded yet.

src/record_syscall.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,11 @@ struct TaskSyscallState {
417417
* original destinations, update registers, etc.
418418
*/
419419
void process_syscall_results();
420+
/**
421+
* Called when a syscall has been completely aborted to undo any changes we
422+
* made.
423+
*/
424+
void abort_syscall_results();
420425

421426
/**
422427
* Upon successful syscall completion, each RestoreAndRecordScratch record
@@ -800,6 +805,34 @@ void TaskSyscallState::process_syscall_results() {
800805
}
801806
}
802807

808+
void TaskSyscallState::abort_syscall_results() {
809+
ASSERT(t, preparation_done);
810+
811+
if (scratch_enabled) {
812+
Registers r = t->regs();
813+
// restore modified in-memory pointers and registers
814+
for (size_t i = 0; i < param_list.size(); ++i) {
815+
auto& param = param_list[i];
816+
if (param.ptr_in_reg) {
817+
r.set_arg(param.ptr_in_reg, param.dest.as_int());
818+
}
819+
if (!param.ptr_in_memory.is_null()) {
820+
set_remote_ptr(t, param.ptr_in_memory, param.dest);
821+
}
822+
}
823+
t->set_regs(r);
824+
} else {
825+
for (auto& param : param_list) {
826+
if (param.mutator) {
827+
size_t size = param.num_bytes.incoming_size;
828+
ASSERT(t, saved_data.size() >= size);
829+
t->write_bytes_helper(param.dest, size, saved_data.data());
830+
saved_data.erase(saved_data.begin(), saved_data.begin() + size);
831+
}
832+
}
833+
}
834+
}
835+
803836
template <typename Arch>
804837
static void prepare_recvmsg(RecordTask* t, TaskSyscallState& syscall_state,
805838
remote_ptr<typename Arch::msghdr> msgp,
@@ -4148,6 +4181,14 @@ Switchable rec_prepare_syscall(RecordTask* t) {
41484181
return syscall_state.done_preparing(s);
41494182
}
41504183

4184+
void rec_abort_prepared_syscall(RecordTask* t) {
4185+
auto syscall_state = syscall_state_property.get(*t);
4186+
if (syscall_state) {
4187+
syscall_state->abort_syscall_results();
4188+
syscall_state_property.remove(*t);
4189+
}
4190+
}
4191+
41514192
template <typename Arch>
41524193
static void rec_prepare_restart_syscall_arch(RecordTask* t,
41534194
TaskSyscallState& syscall_state) {

src/record_syscall.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ class RecordTask;
1515
*/
1616
Switchable rec_prepare_syscall(RecordTask* t);
1717

18+
/**
19+
* Any prepared syscall is not going to happen (because it was aborted by
20+
* tracee seccomp filter). Cancel any preparation work.
21+
*/
22+
void rec_abort_prepared_syscall(RecordTask* t);
23+
1824
/**
1925
* Prepare |t| for its current syscall event to be interrupted and
2026
* possibly restarted.

src/test/seccomp_desched.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ static int pipe_fds[2];
66

77
static void handler(__attribute__((unused)) int sig,
88
__attribute__((unused)) siginfo_t* si,
9-
__attribute__((unused)) void* p) {}
9+
__attribute__((unused)) void* p) {
10+
/* Make a non-buffered syscall to check that it gets recorded OK */
11+
struct statfs fs;
12+
memset(&fs, 0, sizeof(fs));
13+
test_assert(0 == statfs("/", &fs));
14+
test_assert(fs.f_bsize > 0);
15+
}
1016

1117
static void install_filter(void) {
1218
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);

0 commit comments

Comments
 (0)