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
7 changes: 7 additions & 0 deletions accel/tcg/translate-all.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ static void smc_retrans_destory(void)
}
}

void latx_smc_thread_cleanup(void)
{
/* The tree owns its nodes, but the referenced TBs belong to the shared
* translation cache and must remain alive for other guest threads. */
smc_retrans_destory();
}

#endif

/**
Expand Down
4 changes: 4 additions & 0 deletions include/exec/translate-all.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
void tb_invalidate_phys_page(tb_page_addr_t addr);
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);

#ifdef CONFIG_LATX_SMC_OPT
void latx_smc_thread_cleanup(void);
#endif

int get_insn_len_readable(target_ulong address);
int latx_mprotect_one_page_rw(abi_ulong addr);
int latx_mprotect_one_page_rw_resolv(abi_ulong addr);
Expand Down
60 changes: 48 additions & 12 deletions linux-user/guest-seccomp.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,30 @@ typedef struct GuestSeccompData {

typedef struct GuestSeccompFilter {
struct GuestSeccompFilter *previous;
gint references;
unsigned int len;
struct sock_filter insns[];
} GuestSeccompFilter;

GuestSeccompFilter *guest_seccomp_filter_ref(GuestSeccompFilter *filter)
{
if (filter) {
g_atomic_int_inc(&filter->references);
}
return filter;
}

void guest_seccomp_filter_unref(GuestSeccompFilter *filter)
{
/* Each task root and each immutable previous edge owns a reference. */
while (filter && g_atomic_int_dec_and_test(&filter->references)) {
GuestSeccompFilter *previous = filter->previous;

g_free(filter);
filter = previous;
}
}

static bool seccomp_jump_valid(unsigned int pc, uint32_t offset,
unsigned int len)
{
Expand Down Expand Up @@ -284,6 +304,7 @@ static abi_long seccomp_load_filter(abi_ulong target_filter,

filter = g_malloc(sizeof(*filter) + len * sizeof(filter->insns[0]));
filter->previous = NULL;
filter->references = 1;
filter->len = len;
for (i = 0; i < len; i++) {
filter->insns[i].code = tswap16(target_insns[i].code);
Expand Down Expand Up @@ -329,8 +350,6 @@ static abi_long seccomp_install_filter(CPUArchState *env, abi_ulong flags,
if (ret) {
return ret;
}
filter->previous = task->seccomp_filter;

if (flags & SECCOMP_FILTER_FLAG_TSYNC) {
CPUState *other_cpu;

Expand All @@ -339,27 +358,41 @@ static abi_long seccomp_install_filter(CPUArchState *env, abi_ulong flags,
CPU_FOREACH(other_cpu) {
TaskState *other_task = other_cpu->opaque;

/* A CPU under construction inherits the root when published. */
if (!other_task || other_task->seccomp_exiting) {
continue;
}
if (other_task->seccomp_filter != task->seccomp_filter) {
ret = other_task->ts_tid;
ret = other_task->ts_tid ? other_task->ts_tid : -TARGET_EAGAIN;
break;
}
}
if (ret == 0) {
filter->previous = guest_seccomp_filter_ref(task->seccomp_filter);
CPU_FOREACH(other_cpu) {
TaskState *other_task = other_cpu->opaque;

other_task->seccomp_filter = filter;
GuestSeccompFilter *previous;

if (!other_task || other_task->seccomp_exiting) {
continue;
}
previous = other_task->seccomp_filter;
qatomic_store_release(&other_task->seccomp_filter,
guest_seccomp_filter_ref(filter));
guest_seccomp_filter_unref(previous);
}
}
guest_seccomp_filter_unref(filter);
cpu_list_unlock();
end_exclusive();
if (ret != 0) {
g_free(filter);
}
return ret;
}

task->seccomp_filter = filter;
/* Transfer the task's old root reference to the new node's edge. */
cpu_list_lock();
filter->previous = task->seccomp_filter;
qatomic_store_release(&task->seccomp_filter, filter);
cpu_list_unlock();
return 0;
}

Expand All @@ -369,8 +402,8 @@ abi_long guest_seccomp_prctl(CPUArchState *env, abi_long option,
TaskState *task = env_cpu(env)->opaque;

if (option == PR_GET_SECCOMP) {
return task->seccomp_filter ? SECCOMP_MODE_FILTER :
SECCOMP_MODE_DISABLED;
return qatomic_read(&task->seccomp_filter) ? SECCOMP_MODE_FILTER :
SECCOMP_MODE_DISABLED;
}
if (mode != SECCOMP_MODE_FILTER) {
return -TARGET_EINVAL;
Expand Down Expand Up @@ -427,7 +460,10 @@ GuestSeccompAction guest_seccomp_filter_syscall(CPUArchState *env, int num,
abi_long *result)
{
TaskState *task = env_cpu(env)->opaque;
GuestSeccompFilter *filter = task->seccomp_filter;
/* TSYNC stops cpu_exec, not other threads already handling syscalls.
* Publish/read the immutable chain with release/acquire ordering. Old
* chains remain alive through the new chain's owning previous edge. */
GuestSeccompFilter *filter = qatomic_load_acquire(&task->seccomp_filter);
GuestSeccompData data;
uint32_t decision = SECCOMP_RET_ALLOW;
unsigned int i;
Expand Down
5 changes: 5 additions & 0 deletions linux-user/guest-seccomp.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#ifndef LINUX_USER_GUEST_SECCOMP_H
#define LINUX_USER_GUEST_SECCOMP_H

struct GuestSeccompFilter;
struct GuestSeccompFilter *guest_seccomp_filter_ref(
struct GuestSeccompFilter *filter);
void guest_seccomp_filter_unref(struct GuestSeccompFilter *filter);

typedef enum GuestSeccompAction {
GUEST_SECCOMP_CONTINUE,
GUEST_SECCOMP_RETURN,
Expand Down
7 changes: 7 additions & 0 deletions linux-user/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include "qapi/error.h"
#include "qemu.h"
#include "guest-seccomp.h"
#include "qemu/path.h"
#include "qemu/queue.h"
#include "qemu/config-file.h"
Expand Down Expand Up @@ -235,6 +236,12 @@ void fork_end(int child)
Discard information about the parent threads. */
CPU_FOREACH_SAFE(cpu, next_cpu) {
if (cpu != thread_cpu) {
TaskState *task = cpu->opaque;

if (task) {
guest_seccomp_filter_unref(task->seccomp_filter);
task->seccomp_filter = NULL;
}
QTAILQ_REMOVE_RCU(&cpus, cpu, node);
}
}
Expand Down
1 change: 1 addition & 0 deletions linux-user/qemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ typedef struct TaskState {
bool ipc_namespace_isolated;
/* Immutable seccomp filter chain inherited by guest threads. */
struct GuestSeccompFilter *seccomp_filter;
bool seccomp_exiting;
/* A seccomp errno result must not be treated as an internal restart. */
bool seccomp_errno_return;
#ifdef TARGET_X86_64
Expand Down
90 changes: 75 additions & 15 deletions linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
#include "ioctl/mpt3sas_ctl.h"

#include "qemu.h"
#include "exec/translate-all.h"
#include "guest-seccomp.h"
#include "signal-common.h"
#include "qemu/guest-random.h"
Expand Down Expand Up @@ -4567,6 +4568,14 @@ static __thread struct nlmsghdr *pre_nlmh;
static __thread void* buf;
static __thread abi_long all_len;

static void reset_16k_buf(void)
{
free(buf);
buf = NULL;
pre_nlmh = NULL;
all_len = 0;
}

static void set_16k_buf(struct msghdr *msg)
{
buf = malloc(BUFF_16K);
Expand Down Expand Up @@ -4597,9 +4606,7 @@ static abi_long get_from_16k_buf(struct msghdr *msg)
if (NLMSG_OK(nlmh, all_len)) {
pre_nlmh = nlmh;
} else {
pre_nlmh = NULL;
free(buf);
buf = NULL;
reset_16k_buf();
}
return curr_nlmh_len;
}
Expand Down Expand Up @@ -4703,12 +4710,12 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp,
size_t iov_len = msg.msg_iov->iov_len;
set_16k_buf(&msg);
ret = get_errno(safe_recvmsg(fd, &msg, flags));
msg.msg_iov->iov_base = iov_base;
msg.msg_iov->iov_len = iov_len;
if (is_error(ret)) {
assert(0);
reset_16k_buf();
goto out;
}
msg.msg_iov->iov_base = iov_base;
msg.msg_iov->iov_len = iov_len;
all_len = ret;
}
ret = get_from_16k_buf(&msg);
Expand Down Expand Up @@ -9651,6 +9658,36 @@ static void cleanup_guest_thread_resources(CPUArchState *env)
target_munmap(env->gdt.base, sizeof(uint64_t) * TARGET_GDT_ENTRIES, 0);
}

static void cleanup_guest_seccomp(TaskState *ts)
{
/* Keep fork's CPU-list snapshot consistent with the filter references. */
cpu_list_lock();
ts->seccomp_exiting = true;
guest_seccomp_filter_unref(ts->seccomp_filter);
ts->seccomp_filter = NULL;
cpu_list_unlock();
}

/* The child has not started; the caller still owns its CPU and TaskState. */
static void cleanup_failed_guest_thread(CPUArchState *env)
{
CPUState *cpu = env_cpu(env);
TaskState *ts = cpu->opaque;
#ifdef CONFIG_LATX_FAST_JMPCACHE
void *fast_jmp_cache = env->tb_jmp_cache_ptr;
#endif

cleanup_guest_seccomp(ts);
cleanup_guest_thread_resources(env);
object_property_set_bool(OBJECT(cpu), "realized", false, NULL);
object_unparent(OBJECT(cpu));
object_unref(OBJECT(cpu));
#ifdef CONFIG_LATX_FAST_JMPCACHE
latx_fast_jmp_cache_free_rcu(fast_jmp_cache);
#endif
g_free(ts);
}

/* clone_lock is held and at least one other guest thread exists. */
static void QEMU_NORETURN exit_guest_thread_locked(CPUArchState *env)
{
Expand All @@ -9661,6 +9698,7 @@ static void QEMU_NORETURN exit_guest_thread_locked(CPUArchState *env)
void *fast_jmp_cache = x86env->tb_jmp_cache_ptr;
#endif

cleanup_guest_seccomp(ts);
object_property_set_bool(OBJECT(cpu), "realized", false, NULL);
object_unparent(OBJECT(cpu));
object_unref(OBJECT(cpu));
Expand All @@ -9677,6 +9715,13 @@ static void QEMU_NORETURN exit_guest_thread_locked(CPUArchState *env)
}
thread_cpu = NULL;
g_free(ts);
reset_16k_buf();
#ifdef CONFIG_LATX
latx_lsenv_destroy();
#endif
#ifdef CONFIG_LATX_SMC_OPT
latx_smc_thread_cleanup();
#endif
rcu_unregister_thread();
pthread_exit(NULL);
}
Expand Down Expand Up @@ -9770,6 +9815,7 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
TaskState *parent_ts = (TaskState *)cpu->opaque;
new_thread_info info;
pthread_attr_t attr;
int thread_errno = 0;

rcu_start_deferred_thread();

Expand Down Expand Up @@ -9800,11 +9846,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
cpu_clone_regs_child(new_env, newsp, flags);
cpu_clone_regs_parent(env, flags);
new_cpu = env_cpu(new_env);
new_cpu->opaque = ts;
ts->bprm = parent_ts->bprm;
ts->info = parent_ts->info;
ts->signal_mask = parent_ts->signal_mask;
ts->seccomp_filter = parent_ts->seccomp_filter;
ts->ipc_namespace_isolated = parent_ts->ipc_namespace_isolated;

if (flags & CLONE_CHILD_CLEARTID) {
Expand All @@ -9815,16 +9859,28 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
cpu_set_tls (new_env, newtls);
}

/* Publish the initialized task together with its inherited root.
* TSYNC uses this lock and skips CPUs whose task is not yet visible. */
cpu_list_lock();
ts->seccomp_filter = guest_seccomp_filter_ref(parent_ts->seccomp_filter);
new_cpu->opaque = ts;
cpu_list_unlock();

#ifdef CONFIG_LATX_FAST_JMPCACHE
/* cpu_copy copied the parent's pointer; it remains parent-owned. */
new_env->tb_jmp_cache_ptr = NULL;
if (!latx_fast_jmp_cache_init(new_env)) {
cleanup_failed_guest_thread(new_env);
pthread_mutex_unlock(&clone_lock);
errno = ENOMEM;
return -1;
}
#endif
memset(&info, 0, sizeof(info));
pthread_mutex_init(&info.mutex, NULL);
pthread_mutex_lock(&info.mutex);
pthread_cond_init(&info.cond, NULL);
info.env = new_env;
#ifdef CONFIG_LATX_FAST_JMPCACHE
if(!latx_fast_jmp_cache_init(new_env)) {
fprintf(stderr, "[LATX-ERR] latx_fast_jmp_cache_init error!\n");
}
#endif
if (flags & CLONE_CHILD_SETTID) {
info.child_tidptr = child_tidptr;
}
Expand All @@ -9842,7 +9898,6 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
cpu->random_seed = qemu_guest_random_seed_thread_part1();

ret = pthread_create(&info.thread, &attr, clone_func, &info);
/* TODO: Free new CPU state if thread creation failed. */

sigprocmask(SIG_SETMASK, &info.sigmask, NULL);
pthread_attr_destroy(&attr);
Expand All @@ -9851,12 +9906,17 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
pthread_cond_wait(&info.cond, &info.mutex);
ret = info.tid;
} else {
thread_errno = ret;
cleanup_failed_guest_thread(new_env);
ret = -1;
}
pthread_mutex_unlock(&info.mutex);
pthread_cond_destroy(&info.cond);
pthread_mutex_destroy(&info.mutex);
pthread_mutex_unlock(&clone_lock);
if (thread_errno) {
errno = thread_errno;
}
} else {
/* if no CLONE_VM, we consider it is a fork */
if (flags & CLONE_INVALID_FORK_FLAGS) {
Expand Down Expand Up @@ -21204,7 +21264,7 @@ abi_long do_syscall_with_seccomp(void *cpu_env, int num, int seccomp_num,
}

#ifdef CONFIG_LATX_TUNNEL_LIB
suppress_tunnel = ts->seccomp_filter && loader_tunnel;
suppress_tunnel = qatomic_read(&ts->seccomp_filter) && loader_tunnel;
#endif
if (suppress_tunnel) {
ret = 0;
Expand Down
1 change: 1 addition & 0 deletions target/i386/latx/include/latx-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void latx_guest_stack_init(CPUArchState *env);

void latx_init_fpu_regs(CPUArchState *env);
void latx_lsenv_init(CPUArchState *env);
void latx_lsenv_destroy(void);
void latx_dt_init(void);
void ht_pc_thunk_insert(uint32_t thunk_addr, int reg_index);
int ht_pc_thunk_lookup(uint32_t thunk_addr);
Expand Down
1 change: 1 addition & 0 deletions target/i386/latx/include/tu.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ void tu_enough_space(CPUState *cpu);
void tu_trees_reset(void);
TranslationBlock *tu_tree_lookup(target_ulong pc);
void tu_control_init(void);
void tu_control_destroy(void);
TranslationBlock* tb_create(CPUState *cpu, target_ulong pc,
target_ulong cs_base, uint32_t flags, int cflags,
int max_insns, uint16_t bool_flags, TU_TB_START_TYPE mode);
Expand Down
Loading