[Pal/Linux] make _DkTerminateSighandler robust

- atomic access to PAL_TCB_LINUX::pending_event
- replace IN_PAL() condition in _DkTerminateSighandler to avoid race
NOTE: there may still remain races.

Signed-off-by: Isaku Yamahata <isaku.yamahata@gmail.com>
This commit is contained in:
Isaku Yamahata
2019-12-05 15:36:42 -08:00
parent b194aa17fb
commit 9feb7568c9
3 changed files with 22 additions and 11 deletions
+16 -10
View File
@@ -266,17 +266,15 @@ static void _DkTerminateSighandler (int signum, siginfo_t * info,
if (event_num == -1)
return;
uintptr_t rip = uc->uc_mcontext.gregs[REG_RIP];
PAL_TCB_LINUX * tcb = get_tcb_linux();
// If the signal arrives in the middle of a PAL call, add the event
// to pending in the current TCB.
if (ADDR_IN_PAL(rip)) {
PAL_TCB_LINUX * tcb = get_tcb_linux();
if (atomic_read(&tcb->in_pal)) {
assert(tcb);
if (!tcb->pending_event) {
// Use the preserved pending event slot
tcb->pending_event = event_num;
} else {
// try to use the preserved pending event slot
int old = 0;
if (__atomic_compare_exchange_n(&tcb->pending_event, &old, event_num,false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) {
// If there is already a pending event, add the new event to the queue.
// (a relatively rare case.)
struct event_queue * ev = malloc(sizeof(*ev));
@@ -309,6 +307,11 @@ static void _DkPipeSighandler (int signum, siginfo_t * info,
return;
}
void __enter_pal_call(void) {
PAL_TCB_LINUX * tcb = get_tcb_linux();
assert(tcb);
atomic_inc(&tcb->in_pal);
}
/*
* __check_pending_event(): checks the existence of a pending event in the TCB
* and handles the event consequently.
@@ -317,9 +320,12 @@ void __check_pending_event (void)
{
PAL_TCB_LINUX * tcb = get_tcb_linux();
assert(tcb);
if (tcb->pending_event) {
int event = tcb->pending_event;
tcb->pending_event = 0;
if (atomic_add_return(-1, &tcb->in_pal))
return;
int event = __atomic_exchange_n(&tcb->pending_event, 0, __ATOMIC_RELAXED);
if (event) {
_DkGenericSignalHandle(event, NULL, NULL);
if (!LISTP_EMPTY(&tcb->pending_queue)) {
+3
View File
@@ -179,8 +179,11 @@ typedef struct pal_handle
#define HANDLE_TYPE(handle) ((handle)->hdr.type)
extern void __enter_pal_call(void);
extern void __check_pending_event (void);
#define ENTER_PAL_CALL(name) __enter_pal_call()
#define LEAVE_PAL_CALL() do { __check_pending_event(); } while (0)
#define LEAVE_PAL_CALL_RETURN(retval) \
+3 -1
View File
@@ -191,7 +191,9 @@ typedef struct pal_tcb_linux {
PAL_TCB common;
struct {
/* private to Linux PAL */
int pending_event;
struct atomic_int in_pal;
int pending_event; /* needs to be accessed by atomic
* operation */
LISTP_TYPE(event_queue) pending_queue;
PAL_HANDLE handle;
void * alt_stack;