|
From: Jeremy F. <je...@go...> - 2005-01-15 02:07:06
|
CVS commit by fitzhardinge:
What started as a simple signal queueing patch turned into an omnibus
signal rework. Lots of details changed, but the big picture is the
same.
A none/tests/blockfault.c 1.1 [no copyright]
A none/tests/blockfault.stderr.exp 1.1
A none/tests/blockfault.stdout.exp 1.1
A none/tests/blockfault.vgtest 1.1
M +32 -16 coregrind/core.h 1.58
M +11 -2 coregrind/vg_main.c 1.232
M +3 -3 coregrind/vg_mylibc.c 1.109
M +58 -65 coregrind/vg_scheduler.c 1.206
M +249 -52 coregrind/vg_signals.c 1.108
M +9 -5 coregrind/vg_syscalls.c 1.230
M +4 -10 coregrind/linux/core_os.c 1.3
M +69 -2 coregrind/x86/signal.c 1.8
M +7 -2 coregrind/x86-linux/ldt.c 1.7
M +6 -1 coregrind/x86-linux/syscalls.c 1.9
M +3 -3 include/tool.h.base 1.18
M +3 -1 none/tests/Makefile.am 1.56
M +1 -1 none/tests/x86/badseg.c 1.2
--- valgrind/coregrind/core.h #1.57:1.58
@@ -653,4 +653,9 @@ struct _ThreadState {
vki_sigset_t sig_mask;
+ /* A little signal queue for signals we can't get the kernel to
+ queue for us. This is only allocated as needed, since it should
+ be rare. */
+ struct SigQueue *sig_queue;
+
/* Stacks. When a thread slot is freed, we don't deallocate its
stack; we just leave it lying around for the next use of the
@@ -700,17 +705,4 @@ struct _ThreadState {
//ThreadState;
-
-/* When something interesting happens in a signal handler, we need to
- longjmp back into the scheduling loop. This enum is the code which
- is returned via longjmp/setjmp to describe what happened. */
-typedef enum {
- VgSig_None = 0, /* nothing special happened*/
- VgSig_AsyncSig = 1, /* we were signalled externally */
- VgSig_FaultSig, /* an instruction faulted */
- VgSig_FatalSig, /* a fatal signal */
- VgSig_Exiting, /* we're exiting */
-} VgSigCode;
-
-
/* The thread table. */
extern ThreadState VG_(threads)[VG_N_THREADS];
@@ -795,6 +787,17 @@ extern void VG_(pp_sched_status) ( void
// Longjmp back to the scheduler and thus enter the sighandler immediately.
-extern void VG_(resume_scheduler) ( ThreadId tid, VgSigCode code,
- Int sigNo, const vki_siginfo_t *info );
+extern void VG_(resume_scheduler) ( ThreadId tid );
+
+/* Copy the state of a thread from VG_(baseBlock), presumably after it
+ has been descheduled. For sanity-check purposes, fill the vacated
+ VG_(baseBlock) with garbage so as to make the system more likely to
+ fail quickly if we erroneously continue to poke around inside
+ VG_(baseBlock) without first doing a load_thread_state().
+*/
+void VG_(save_thread_state) ( ThreadId tid );
+
+/* Copy the saved state of a thread into VG_(baseBlock), ready for it
+ to be run. */
+void VG_(load_thread_state) ( ThreadId tid );
/* The red-zone size which we put at the bottom (highest address) of
@@ -843,4 +846,7 @@ extern Int VG_(max_signal);
extern void VG_(sigstartup_actions) ( void );
+/* Modify a thread's state so that when it next runs it will be
+ running in the signal handler (or doing the default action if there
+ is none). */
extern void VG_(deliver_signal) ( ThreadId tid, const vki_siginfo_t * );
@@ -852,5 +858,5 @@ extern void VG_(poll_signals) ( ThreadId
/* Fake system calls for signal handling. */
extern void VG_(do_sys_sigaltstack) ( ThreadId tid );
-extern Int VG_(do_sys_sigaction) ( ThreadId tid, Int signo,
+extern Int VG_(do_sys_sigaction) ( Int signo,
const struct vki_sigaction *new_act,
struct vki_sigaction *old_act );
@@ -882,4 +888,14 @@ extern void VG_(synth_fault_perms) (Thr
extern void VG_(get_sigstack_bounds)( Addr* low, Addr* high );
+/* Extend the stack to cover addr, if possible */
+extern Bool VG_(extend_stack)(Addr addr, UInt maxsize);
+
+/* Returns True if the signal is OK for the client to use */
+extern Bool VG_(client_signal_OK)(Int sigNo);
+
+/* Forces the client's signal handler to SIG_DFL - generally just
+ before using that signal to kill the process. */
+extern void VG_(set_default_handler)(Int sig);
+
/* ---------------------------------------------------------------------
Exports of vg_mylibc.c
--- valgrind/coregrind/vg_scheduler.c #1.205:1.206
@@ -278,16 +278,4 @@ ThreadId VG_(get_lwp_tid)(Int lwp)
}
-/* Copy the saved state of a thread into VG_(baseBlock), ready for it
- to be run. */
-static void load_thread_state ( ThreadId tid )
-{
- vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
-
- VGA_(load_state)(&VG_(threads)[tid].arch, tid);
-
- vg_tid_currently_in_baseBlock = tid;
- vg_tid_last_in_baseBlock = tid;
-}
-
/*
Mark a thread as Runnable. This will block until the run_sema is
@@ -457,5 +445,5 @@ void VG_(vg_yield)(void)
VG_(baseBlock) without first doing a load_thread_state().
*/
-static void save_thread_state ( ThreadId tid )
+void VG_(save_thread_state) ( ThreadId tid )
{
vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID);
@@ -466,7 +454,18 @@ static void save_thread_state ( ThreadId
}
+/* Copy the saved state of a thread into VG_(baseBlock), ready for it
+ to be run. */
+void VG_(load_thread_state) ( ThreadId tid )
+{
+ vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID);
-void VG_(resume_scheduler)(ThreadId tid, VgSigCode why,
- Int sigNo, const vki_siginfo_t *info)
+ VGA_(load_state)(&VG_(threads)[tid].arch, tid);
+
+ vg_tid_currently_in_baseBlock = tid;
+ vg_tid_last_in_baseBlock = tid;
+}
+
+
+void VG_(resume_scheduler)(ThreadId tid)
{
ThreadState *tst = VG_(get_ThreadState)(tid);
@@ -477,23 +476,20 @@ void VG_(resume_scheduler)(ThreadId tid,
/* Can't continue; must longjmp back to the scheduler and thus
enter the sighandler immediately. */
- tst->siginfo.si_signo = sigNo;
- if (info != NULL)
- VG_(memcpy)(&tst->siginfo, info, sizeof(vki_siginfo_t));
- LONGJMP(tst->sched_jmpbuf, why);
+ LONGJMP(tst->sched_jmpbuf, True);
}
}
-#define SCHEDSETJMP(tid, sigcode, stmt) \
+#define SCHEDSETJMP(tid, jumped, stmt) \
do { \
ThreadState * volatile _qq_tst = VG_(get_ThreadState)(tid); \
\
- sigcode = SETJMP(_qq_tst->sched_jmpbuf); \
- if ((sigcode) == 0) { \
+ (jumped) = SETJMP(_qq_tst->sched_jmpbuf); \
+ if ((jumped) == 0) { \
vg_assert(!_qq_tst->sched_jmpbuf_valid); \
_qq_tst->sched_jmpbuf_valid = True; \
stmt; \
} else if (VG_(clo_trace_sched)) \
- VG_(printf)("SCHEDSETJMP(line %d) tid %d, sigcode=%d\n", __LINE__, tid, sigcode); \
+ VG_(printf)("SCHEDSETJMP(line %d) tid %d, jumped=%d\n", __LINE__, tid, jumped); \
vg_assert(_qq_tst->sched_jmpbuf_valid); \
_qq_tst->sched_jmpbuf_valid = False; \
@@ -506,28 +502,35 @@ UInt run_thread_for_a_while ( ThreadId t
{
volatile UInt trc = 0;
- VgSigCode sigcode;
+ Bool jumped;
vg_assert(VG_(is_valid_tid)(tid));
vg_assert(VG_(is_running_thread)(tid));
+ vg_assert(!VG_(is_exiting)(tid));
VGP_PUSHCC(VgpRun);
- load_thread_state ( tid );
+ VG_(load_thread_state) ( tid );
/* there should be no undealt-with signals */
- vg_assert(VG_(threads)[tid].siginfo.si_signo == 0);
+ //vg_assert(VG_(threads)[tid].siginfo.si_signo == 0);
- //VG_(printf)("running EIP = %p\n", VG_(threads)[tid].arch.m_eip);
+ //VG_(printf)("running EIP = %p ESP=%p\n", VG_(threads)[tid].arch.m_eip, VG_(threads)[tid].arch.m_esp);
- SCHEDSETJMP(tid, sigcode, trc = VG_(run_innerloop)());
+ SCHEDSETJMP(tid, jumped, trc = VG_(run_innerloop)());
- if (sigcode != VgSig_None) {
+ if (jumped) {
/* We get here if the client took a fault, which caused our
signal handler to longjmp. */
- vg_assert(sigcode == VgSig_FaultSig); /* or fatal... */
vg_assert(trc == 0);
trc = VG_TRC_FAULT_SIGNAL;
+ VG_(sigprocmask)(VKI_SIG_SETMASK, &VG_(blocked_mask), NULL);
}
- save_thread_state ( tid );
+ /* If we were delivered a signal, then the ThreadState registers
+ may already be up to date. */
+ if (VG_(is_VCPU_thread)(tid))
+ VG_(save_thread_state) ( tid );
+ else
+ vg_assert(VG_(get_VCPU_tid)() == VG_INVALID_THREADID);
+
VGP_POPCC(VgpRun);
return trc;
@@ -538,4 +541,6 @@ static
void mostly_clear_thread_record ( ThreadId tid )
{
+ vki_sigset_t savedmask;
+
vg_assert(tid >= 0 && tid < VG_N_THREADS);
VGA_(clear_thread)(&VG_(threads)[tid].arch);
@@ -551,4 +556,12 @@ void mostly_clear_thread_record ( Thread
VG_(threads)[tid].altstack.ss_flags = VKI_SS_DISABLE;
+ /* clear out queued signals */
+ VG_(block_all_host_signals)(&savedmask);
+ if (VG_(threads)[tid].sig_queue != NULL) {
+ VG_(arena_free)(VG_AR_CORE, VG_(threads)[tid].sig_queue);
+ VG_(threads)[tid].sig_queue = NULL;
+ }
+ VG_(restore_all_host_signals)(&savedmask);
+
VG_(threads)[tid].sched_jmpbuf_valid = False;
}
@@ -590,4 +603,6 @@ void VG_(scheduler_init) ( void )
for (i = 0 /* NB; not 1 */; i < VG_N_THREADS; i++) {
+ VG_(threads)[i].sig_queue = NULL;
+
VGA_(os_state_init)(&VG_(threads)[i]);
mostly_clear_thread_record(i);
@@ -608,5 +624,5 @@ void VG_(scheduler_init) ( void )
VGA_(init_thread)(&VG_(threads)[tid_main].arch);
- save_thread_state ( tid_main );
+ VG_(save_thread_state) ( tid_main );
/* Initial thread's stack is the original process stack */
@@ -653,5 +669,5 @@ static void handle_syscall(ThreadId tid)
{
ThreadState *tst = VG_(get_ThreadState)(tid);
- VgSigCode sigcode;
+ Bool jumped;
/* Syscall may or may not block; either way, it will be
@@ -659,5 +675,5 @@ static void handle_syscall(ThreadId tid)
runnable again. We could take a signal while the
syscall runs. */
- SCHEDSETJMP(tid, sigcode, VG_(client_syscall)(tid));
+ SCHEDSETJMP(tid, jumped, VG_(client_syscall)(tid));
if (!VG_(is_running_thread)(tid))
@@ -667,31 +682,8 @@ static void handle_syscall(ThreadId tid)
vg_assert(VG_(is_running_thread)(tid));
- switch(sigcode) {
- case VgSig_None:
- break; /* nothing */
-
- case VgSig_Exiting:
- vg_assert(VG_(is_exiting)(tid));
- break;
-
- case VgSig_AsyncSig:
- case VgSig_FatalSig:
- if (sigcode == VgSig_FatalSig) {
- /* was fatal, we're exiting */
- vg_assert(VG_(is_exiting)(tid));
- }
-
+ if (jumped) {
VG_(sigprocmask)(VKI_SIG_SETMASK, &VG_(blocked_mask), NULL);
-
- tst->siginfo.si_signo = 0; /* don't care about signal state */
- break;
-
- case VgSig_FaultSig:
- /* We should never get an instruction fault from doing a
- syscall. */
- vg_assert(0);
+ VG_(poll_signals)(tid);
}
-
- vg_assert(tst->siginfo.si_signo == 0);
}
@@ -782,5 +774,9 @@ VgSchedReturnCode VG_(scheduler) ( Threa
/* Explicit yield, because they're expecting to be
spinning. */
- VG_(vg_yield)();
+ //VG_(vg_yield)();
+ VG_(dispatch_ctr) = 0;
+ //VG_(set_sleeping)(tid, VgTs_Yielding);
+ //VG_(do_syscall)(__NR_sched_yield);
+ //VG_(set_running)(tid);
break;
@@ -796,7 +792,4 @@ VgSchedReturnCode VG_(scheduler) ( Threa
sneak in). */
VG_(sigprocmask)(VKI_SIG_SETMASK, &VG_(blocked_mask), NULL);
-
- VG_(deliver_signal)(tid, &tst->siginfo);
- tst->siginfo.si_signo = 0; /* done */
break;
--- valgrind/coregrind/vg_signals.c #1.107:1.108
@@ -103,4 +103,11 @@ vki_sigset_t VG_(blocked_mask);
Int VG_(max_signal) = _VKI_NSIG;
+#define N_QUEUED_SIGNALS 4
+
+typedef struct SigQueue {
+ Int next;
+ vki_siginfo_t sigs[N_QUEUED_SIGNALS];
+} SigQueue;
+
/* ---------------------------------------------------------------------
HIGH LEVEL STUFF TO DO WITH SIGNALS: POLICY (MOSTLY)
@@ -452,15 +459,13 @@ void VG_(do_sys_sigaltstack) ( ThreadId
-Int VG_(do_sys_sigaction) ( ThreadId tid, Int signo,
+Int VG_(do_sys_sigaction) ( Int signo,
const struct vki_sigaction *new_act,
struct vki_sigaction *old_act )
{
- vg_assert(VG_(is_valid_tid)(tid));
-
if (VG_(clo_trace_signals))
VG_(message)(Vg_DebugExtraMsg,
- "sys_sigaction: tid %d, sigNo %d, "
+ "sys_sigaction: sigNo %d, "
"new %p, old %p, new flags 0x%llx",
- tid, signo, (UWord)new_act, (UWord)old_act,
+ signo, (UWord)new_act, (UWord)old_act,
(ULong)(new_act ? new_act->sa_flags : 0) );
@@ -674,4 +679,13 @@ void VG_(restore_all_host_signals) ( /*
}
+Bool VG_(client_signal_OK)(Int sigNo)
+{
+ Bool ret = sigNo >= 1 && sigNo <= VKI_SIGVGRTUSERMAX;
+
+ //VG_(printf)("client_signal_OK(%d) -> %d\n", sigNo, ret);
+
+ return ret;
+}
+
/* ---------------------------------------------------------------------
The signal simulation proper. A simplified version of what the
@@ -722,4 +736,7 @@ void vg_push_signal_frame ( ThreadId tid
vg_assert(vg_scss.scss_per_sig[sigNo].scss_handler != VKI_SIG_DFL);
+ /* This may fail if the client stack is busted; if that happens,
+ the whole process will exit rather than simply calling the
+ signal handler. */
VGA_(push_signal_frame)(tid, esp_top_of_frame, siginfo,
vg_scss.scss_per_sig[sigNo].scss_handler,
@@ -1342,4 +1359,8 @@ static void synth_fault_common(ThreadId
info._sifields._sigfault._addr = (void*)addr;
+ /* If they're trying to block the signal, force it to be delivered */
+ if (VG_(sigismember)(&VG_(threads)[tid].sig_mask, VKI_SIGSEGV))
+ VG_(set_default_handler)(VKI_SIGSEGV);
+
VG_(deliver_signal)(tid, &info);
}
@@ -1402,6 +1423,5 @@ void VG_(deliver_signal) ( ThreadId tid,
the signal to get through no matter what; if they're ignoring
it, then we do this override (this is so we can send it SIGSEGV,
- etc).
- */
+ etc). */
handler_fn = handler->scss_handler;
if (handler_fn == VKI_SIG_IGN)
@@ -1416,6 +1436,17 @@ void VG_(deliver_signal) ( ThreadId tid,
%EIP so that when execution continues, we will enter the
signal handler with the frame on top of the client's stack,
- as it expects. */
+ as it expects.
+
+ Signal delivery can fail if the client stack is too small or
+ missing, and we can't push the frame. If that happens,
+ push_signal_frame will cause the whole process to exit when
+ we next hit the scheduler.
+ */
vg_assert(VG_(is_valid_tid)(tid));
+
+ /* Before we muck with the thread's state, we need to work out where it is. */
+ if (VG_(is_VCPU_thread)(tid)) {
+ VG_(save_thread_state)(tid);
+ }
vg_push_signal_frame ( tid, info );
@@ -1441,4 +1472,83 @@ void VG_(deliver_signal) ( ThreadId tid,
}
+/* Make a signal pending for a thread, for later delivery.
+ VG_(poll_signals) will arrange for it to be delivered at the right
+ time.
+
+ tid==0 means add it to the process-wide queue, and not sent it to a
+ specific thread.
+*/
+void queue_signal(ThreadId tid, const vki_siginfo_t *si)
+{
+ ThreadState *tst;
+ SigQueue *sq;
+ vki_sigset_t savedmask;
+
+ tst = VG_(get_ThreadState)(tid);
+
+ /* Protect the signal queue against async deliveries */
+ VG_(block_all_host_signals)(&savedmask);
+
+ if (tst->sig_queue == NULL) {
+ tst->sig_queue = VG_(arena_malloc)(VG_AR_CORE, sizeof(*tst->sig_queue));
+ VG_(memset)(tst->sig_queue, 0, sizeof(*tst->sig_queue));
+ }
+ sq = tst->sig_queue;
+
+ if (VG_(clo_trace_signals))
+ VG_(message)(Vg_DebugMsg, "Queueing signal %d (idx %d) to thread %d",
+ si->si_signo, sq->next, tid);
+
+ /* Add signal to the queue. If the queue gets overrun, then old
+ queued signals may get lost. */
+ if (sq->sigs[sq->next].si_signo != 0)
+ VG_(message)(Vg_UserMsg, "Signal %d being dropped from thread %d's queue\n",
+ sq->sigs[sq->next].si_signo, tid);
+
+ sq->sigs[sq->next] = *si;
+ sq->next = (sq->next+1) % N_QUEUED_SIGNALS;
+
+ VG_(restore_all_host_signals)(&savedmask);
+}
+
+/*
+ Returns the next queued signal for thread tid which is in "set".
+ tid==0 means process-wide signal. Set si_signo to 0 when the
+ signal has been delivered.
+
+ Must be called with all signals blocked, to protect against async
+ deliveries.
+*/
+static vki_siginfo_t *next_queued(ThreadId tid, const vki_sigset_t *set)
+{
+ ThreadState *tst = VG_(get_ThreadState)(tid);
+ SigQueue *sq;
+ Int idx;
+ vki_siginfo_t *ret = NULL;
+
+ sq = tst->sig_queue;
+ if (sq == NULL)
+ goto out;
+
+ idx = sq->next;
+ do {
+ if (0)
+ VG_(printf)("idx=%d si_signo=%d inset=%d\n", idx,
+ sq->sigs[idx].si_signo, VG_(sigismember)(set, sq->sigs[idx].si_signo));
+
+ if (sq->sigs[idx].si_signo != 0 && VG_(sigismember)(set, sq->sigs[idx].si_signo)) {
+ if (VG_(clo_trace_signals))
+ VG_(message)(Vg_DebugMsg, "Returning queued signal %d (idx %d) for thread %d",
+ sq->sigs[idx].si_signo, idx, tid);
+ ret = &sq->sigs[idx];
+ goto out;
+ }
+
+ idx = (idx + 1) % N_QUEUED_SIGNALS;
+ } while(idx != sq->next);
+ out:
+ return ret;
+}
+
/*
Receive an async signal from the kernel.
@@ -1458,4 +1568,8 @@ void vg_async_signalhandler ( Int sigNo,
VG_(set_running)(tid);
+ if (VG_(clo_trace_signals))
+ VG_(message)(Vg_DebugMsg, "Async handler got signal %d for tid %d info %d",
+ sigNo, tid, info->si_code);
+
/* Update thread state properly if this signal happened in or around a syscall */
VGA_(interrupted_syscall)(&tst->arch, uc,
@@ -1468,9 +1582,64 @@ void vg_async_signalhandler ( Int sigNo,
handler.
*/
- VG_(resume_scheduler)(tid, VgSig_AsyncSig, sigNo, info);
+ VG_(resume_scheduler)(tid);
VG_(core_panic)("vg_async_signalhandler: got unexpected signal while outside of scheduler");
}
+/* Extend the stack to cover addr. maxsize is the limit the stack can grow to.
+
+ Returns True on success, False on failure.
+
+ Succeeds without doing anything if addr is already within a segment.
+
+ Failure could be caused by:
+ - addr not below a growable segment
+ - new stack size would exceed maxsize
+ - mmap failed for some other reason
+ */
+Bool VG_(extend_stack)(Addr addr, UInt maxsize)
+{
+ Segment *seg;
+ Addr base;
+ UInt newsize;
+
+ /* Find the next Segment above addr */
+ seg = VG_(find_segment)(addr);
+ if (seg == NULL)
+ seg = VG_(first_segment)();
+ else if (VG_(seg_contains)(seg, addr, sizeof(void *)))
+ return True;
+ else
+ seg = VG_(next_segment)(seg);
+
+ /* If there isn't one, or it isn't growable, fail */
+ if (seg == NULL ||
+ !(seg->flags & SF_GROWDOWN) ||
+ VG_(seg_contains)(seg, addr, sizeof(void *)))
+ return False;
+
+ vg_assert(seg->addr > addr);
+
+ /* Create the mapping */
+ base = PGROUNDDN(addr);
+ newsize = seg->addr - base;
+
+ if (seg->len + newsize >= maxsize)
+ return False;
+
+ if (VG_(mmap)((Char *)base, newsize,
+ seg->prot,
+ VKI_MAP_PRIVATE | VKI_MAP_FIXED | VKI_MAP_ANONYMOUS | VKI_MAP_CLIENT,
+ seg->flags,
+ -1, 0) == (void *)-1)
+ return False;
+
+ if (0)
+ VG_(printf)("extended stack: %p %d\n",
+ base, newsize);
+
+ return True;
+}
+
/*
Recieve a sync signal from the host.
@@ -1481,11 +1650,4 @@ void vg_sync_signalhandler ( Int sigNo,
ThreadId tid = VG_(get_lwp_tid)(VG_(gettid)());
- if (!VG_(is_running_thread)(tid)) {
- /* This may possibly happen if someone sent us one of the sync
- signals with a kill syscall. Get the CPU before going on. */
- vg_assert(info->si_code <= VKI_SI_USER);
- VG_(set_running)(tid);
- }
-
vg_assert(info != NULL);
vg_assert(info->si_signo == sigNo);
@@ -1495,7 +1657,22 @@ void vg_sync_signalhandler ( Int sigNo,
sigNo == VKI_SIGILL);
+ if (!VG_(is_running_thread)(tid)) {
+ /* This may possibly happen if someone sent us one of the sync
+ signals with a kill syscall. Get the CPU before going on. */
+ vg_assert(info->si_code <= VKI_SI_USER);
+ VG_(set_running)(tid);
+
+ /* Since every thread has these signals unblocked, we can't rely
+ on the kernel to route them properly, so we need to queue
+ them manually. */
+ queue_signal(0, info);
+ VG_(resume_scheduler)(tid);
+ }
+
if (VG_(clo_trace_signals)) {
- VG_(message)(Vg_DebugMsg, "signal %d arrived ... si_code=%d, EIP=%p",
- sigNo, info->si_code, ARCH_INSTR_PTR(VG_(threads)[tid].arch) );
+ VG_(message)(Vg_DebugMsg, "signal %d arrived ... si_code=%d, EIP=%p, eip=%p",
+ sigNo, info->si_code,
+ ARCH_INSTR_PTR(VG_(threads)[tid].arch),
+ UCONTEXT_INSTR_PTR(uc) );
}
vg_assert(sigNo >= 1 && sigNo <= VG_(max_signal));
@@ -1534,8 +1711,5 @@ void vg_sync_signalhandler ( Int sigNo,
if (info->si_code == 1 && /* SEGV_MAPERR */
- seg != NULL &&
- fault >= esp &&
- fault < seg->addr &&
- (seg->flags & SF_GROWDOWN)) {
+ fault >= esp) {
/* If the fault address is above esp but below the current known
stack segment base, and it was a fault because there was
@@ -1543,17 +1717,12 @@ void vg_sync_signalhandler ( Int sigNo,
then extend the stack segment.
*/
- Addr base = PGROUNDDN(esp);
- if (seg->len + (seg->addr - base) <= VG_(threads)[tid].stack_size &&
- (void*)-1 != VG_(mmap)((Char *)base, seg->addr - base,
- VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC,
- VKI_MAP_PRIVATE|VKI_MAP_FIXED|VKI_MAP_ANONYMOUS|VKI_MAP_CLIENT,
- SF_STACK|SF_GROWDOWN,
- -1, 0)) {
+ if (VG_(extend_stack)(fault, VG_(threads)[tid].stack_size)) {
if (VG_(clo_trace_signals))
VG_(message)(Vg_DebugMsg,
- " -> extended stack base to %p", base);
+ " -> extended stack base to %p", PGROUNDDN(fault));
return; // extension succeeded, restart instruction
} else
- VG_(message)(Vg_UserMsg, "Stack overflow in thread %d", tid);
+ VG_(message)(Vg_UserMsg, "Stack overflow in thread %d: can't grow stack to %p",
+ tid, fault);
/* Fall into normal signal handling for all other cases */
@@ -1580,22 +1749,23 @@ void vg_sync_signalhandler ( Int sigNo,
}
- if (info->si_code <= VKI_SI_USER) {
- /*
- OK, one of sync signals was sent async by user-mode, so try
- to deliver it to someone who cares.
-
- XXX For now, just drop it.
- */
- VG_(message)(Vg_UserMsg,
- "dropping signal %s sent by pid %d",
- signame(sigNo), info->_sifields._kill._pid);
- } else {
+ /* OK, this is a signal we really have to deal with. If it came
+ from the client's code, then we can jump back into the scheduler
+ and have it delivered. Otherwise it's a Valgrind bug. */
+ {
Addr context_ip;
Char buf[1024];
- ThreadState *tst;
+ ThreadState *tst = VG_(get_ThreadState)(VG_(get_lwp_tid)(VG_(gettid)()));
+
+ if (VG_(sigismember)(&tst->sig_mask, sigNo)) {
+ /* signal is blocked, but they're not allowed to block faults */
+ VG_(set_default_handler)(sigNo);
+ }
/* Can't continue; must longjmp back to the scheduler and thus
enter the sighandler immediately. */
- VG_(resume_scheduler)(tid, VgSig_FaultSig, sigNo, info);
+ VG_(deliver_signal)(tid, info);
+ VG_(resume_scheduler)(tid);
+
+
/* If resume_scheduler returns, it means we don't have longjmp
@@ -1656,10 +1826,5 @@ static void sigvgkill_handler(int signo,
VG_(set_running)(tid);
- /* Check that the signal comes from within, and ignore it if not. */
- if (si->si_code != VKI_SI_TKILL ||
- VG_(get_lwp_tid)(si->_sifields._kill._pid) == VG_INVALID_THREADID)
- return;
-
- VG_(resume_scheduler)(tid, VgSig_Exiting, 0, 0);
+ VG_(resume_scheduler)(tid);
VG_(core_panic)("sigvgkill_handler couldn't return to the scheduler\n");
@@ -1686,4 +1851,19 @@ void pp_vg_ksigaction ( struct vki_sigac
/*
+ Force signal handler to default
+ */
+void VG_(set_default_handler)(Int signo)
+{
+ struct vki_sigaction sa;
+
+ sa.ksa_handler = VKI_SIG_DFL;
+ sa.sa_flags = 0;
+ sa.sa_restorer = 0;
+ VG_(sigemptyset)(&sa.sa_mask);
+
+ VG_(do_sys_sigaction)(signo, &sa, NULL);
+}
+
+/*
Poll for pending signals, and set the next one up for delivery.
*/
@@ -1691,8 +1871,9 @@ void VG_(poll_signals)(ThreadId tid)
{
static const struct vki_timespec zero = { 0, 0 };
- vki_siginfo_t si;
+ vki_siginfo_t si, *sip;
vki_sigset_t pollset;
ThreadState *tst = VG_(get_ThreadState)(tid);
Int i;
+ vki_sigset_t saved_mask;
/* look for all the signals this thread isn't blocking */
@@ -1702,4 +1883,20 @@ void VG_(poll_signals)(ThreadId tid)
VG_(sigdelset)(&pollset, VKI_SIGVGCHLD); /* never look for this */
+ //VG_(printf)("tid %d pollset=%08x%08x\n", tid, pollset.sig[1], pollset.sig[0]);
+
+ VG_(block_all_host_signals)(&saved_mask);
+
+ /* First look for any queued pending signals */
+ sip = next_queued(0, &pollset); /* process-wide */
+ if (sip == NULL)
+ sip = next_queued(tid, &pollset); /* this thread */
+ if (sip != NULL) {
+ VG_(deliver_signal)(tid, sip);
+ sip->si_signo = 0;
+ VG_(restore_all_host_signals)(&saved_mask);
+ return;
+ }
+ VG_(restore_all_host_signals)(&saved_mask);
+
/* Grab a single pending signal for this thread, and deliver it to
the thread */
--- valgrind/coregrind/vg_mylibc.c #1.108:1.109
@@ -60,5 +60,5 @@ Int VG_(sigemptyset)( vki_sigset_t* set
}
-Bool VG_(isemptysigset)( vki_sigset_t* set )
+Bool VG_(isemptysigset)( const vki_sigset_t* set )
{
Int i;
@@ -69,5 +69,5 @@ Bool VG_(isemptysigset)( vki_sigset_t* s
}
-Bool VG_(isfullsigset)( vki_sigset_t* set )
+Bool VG_(isfullsigset)( const vki_sigset_t* set )
{
Int i;
@@ -101,5 +101,5 @@ Int VG_(sigdelset)( vki_sigset_t* set, I
}
-Int VG_(sigismember) ( vki_sigset_t* set, Int signum )
+Int VG_(sigismember) ( const vki_sigset_t* set, Int signum )
{
if (set == NULL)
--- valgrind/coregrind/vg_main.c #1.231:1.232
@@ -2697,7 +2697,16 @@ int main(int argc, char **argv, char **e
// p: segment stuff [otherwise get seg faults...]
//--------------------------------------------------------------
+ {
+ Segment *seg;
VG_(mprotect)( (void *)VG_(client_trampoline_code),
VG_(trampoline_code_length), VKI_PROT_READ|VKI_PROT_EXEC );
+ /* Make sure this segment isn't treated as stack */
+ seg = VG_(find_segment)(VG_(client_trampoline_code));
+ if (seg && VG_(seg_contains)(seg, VG_(client_trampoline_code),
+ VG_(trampoline_code_length)))
+ seg->flags &= ~(SF_STACK | SF_GROWDOWN);
+ }
+
//==============================================================
// Can use VG_(map)() after segments set up
--- valgrind/coregrind/vg_syscalls.c #1.229:1.230
@@ -1935,4 +1935,8 @@ PRE(sys_fcntl, 0)
case VKI_F_SETSIG:
case VKI_F_SETLEASE:
+ if (arg2 == VKI_F_SETSIG && !VG_(client_signal_OK)(arg3)) {
+ set_result(-VKI_EINVAL);
+ return;
+ }
PRE_REG_READ3(long, "fcntl",
unsigned int, fd, unsigned int, cmd, unsigned long, arg);
@@ -4074,5 +4078,5 @@ PRE(sys_tkill, 0)
PRINT("sys_tkill ( %d, %d )", arg1,arg2);
PRE_REG_READ2(long, "tkill", int, tid, int, sig);
- if (arg2 == VKI_SIGVGKILL || arg2 == VKI_SIGVGCHLD)
+ if (!VG_(client_signal_OK)(arg2))
set_result( -VKI_EINVAL );
}
@@ -4092,5 +4096,5 @@ PRE(sys_tgkill, 0)
PRINT("sys_tgkill ( %d, %d, %d )", arg1,arg2,arg3);
PRE_REG_READ3(long, "tgkill", int, tgid, int, tid, int, sig);
- if (arg2 == VKI_SIGVGKILL || arg2 == VKI_SIGVGCHLD)
+ if (!VG_(client_signal_OK)(arg3))
set_result( -VKI_EINVAL );
}
@@ -4110,5 +4114,5 @@ PRE(sys_kill, 0)
PRINT("sys_kill ( %d, %d )", arg1,arg2);
PRE_REG_READ2(long, "kill", int, pid, int, sig);
- if (arg2 == VKI_SIGVGKILL || arg2 == VKI_SIGVGCHLD)
+ if (!VG_(client_signal_OK)(arg2))
set_result( -VKI_EINVAL );
}
@@ -5422,5 +5426,5 @@ PRE(sys_sigaction, Special)
}
- set_result(VG_(do_sys_sigaction)(tid, arg1, newp, oldp));
+ set_result(VG_(do_sys_sigaction)(arg1, newp, oldp));
if (arg3 != 0 && SYSRES == 0) {
@@ -5453,5 +5457,5 @@ PRE(sys_rt_sigaction, Special)
PRE_MEM_WRITE( "rt_sigaction(oldact)", arg3, sizeof(struct vki_sigaction));
- set_result(VG_(do_sys_sigaction)(tid, arg1, (const struct vki_sigaction *)arg2,
+ set_result(VG_(do_sys_sigaction)(arg1, (const struct vki_sigaction *)arg2,
(struct vki_sigaction *)arg3));
}
--- valgrind/coregrind/x86/signal.c #1.7:1.8
@@ -343,4 +343,47 @@ static void synth_ucontext(ThreadId tid,
post_reg_write_deliver_signal)
+static Bool seg_ok(Segment *seg, VgSigFrame *frame)
+{
+ if (!VG_(seg_overlaps)(seg, (Addr)frame, sizeof(*frame)))
+ return False;
+
+ if ((seg->prot & (VKI_PROT_READ|VKI_PROT_WRITE)) != (VKI_PROT_READ|VKI_PROT_WRITE))
+ return False;
+
+ return True;
+}
+
+/* Make sure that there are segments covering all of frame, and
+ they're read/write. */
+static Bool compat_segments(Segment *seg, VgSigFrame *frame)
+{
+ Segment *next;
+ Addr a = (Addr)frame;
+ UInt remains = sizeof(*frame);
+
+ while(remains && seg) {
+ Addr end = seg->addr + seg->len;
+
+ next = VG_(next_segment)(seg);
+
+ if (!seg_ok(seg, frame))
+ return False;
+
+ if ((end - a) > remains)
+ remains = 0;
+ else {
+ remains -= end - a;
+ a = end;
+ }
+
+ if (next->addr != end)
+ next = NULL;
+
+ seg = next;
+ }
+
+ return remains == 0;
+}
+
void VGA_(push_signal_frame)(ThreadId tid, Addr esp_top_of_frame,
const vki_siginfo_t *siginfo,
@@ -349,7 +392,8 @@ void VGA_(push_signal_frame)(ThreadId ti
{
Addr esp;
- ThreadState* tst;
+ ThreadState* tst = VG_(get_ThreadState)(tid);
VgSigFrame* frame;
Int sigNo = siginfo->si_signo;
+ Segment *stackseg = NULL;
esp = esp_top_of_frame;
@@ -357,5 +401,28 @@ void VGA_(push_signal_frame)(ThreadId ti
frame = (VgSigFrame*)esp;
- tst = & VG_(threads)[tid];
+ if (VG_(extend_stack)((Addr)frame, tst->stack_size)) {
+ stackseg = VG_(find_segment)((Addr)frame);
+ if (0 && stackseg)
+ VG_(printf)("frame=%p seg=%p-%p\n",
+ frame, stackseg->addr, stackseg->addr+stackseg->len);
+ }
+
+ if (stackseg == NULL || !compat_segments(stackseg, frame)) {
+ VG_(message)(Vg_UserMsg,
+ "Can't extend stack to %p during signal delivery for thread %d:",
+ frame, tid);
+ if (stackseg == NULL)
+ VG_(message)(Vg_UserMsg, " no stack segment");
+ else
+ VG_(message)(Vg_UserMsg, " too small or bad protection modes");
+
+ /* set SIGSEGV to default handler */
+ VG_(set_default_handler)(VKI_SIGSEGV);
+ VG_(synth_fault_mapping)(tid, (Addr)frame);
+
+ /* The whole process should be about to die, since the default
+ action of SIGSEGV to kill the whole process. */
+ return;
+ }
/* For tracking memory events, indicate the entire frame has been
--- valgrind/include/tool.h.base #1.17:1.18
@@ -533,10 +533,10 @@
extern Int VG_(sigemptyset) ( vki_sigset_t* set );
-extern Bool VG_(isfullsigset) ( vki_sigset_t* set );
-extern Bool VG_(isemptysigset) ( vki_sigset_t* set );
+extern Bool VG_(isfullsigset) ( const vki_sigset_t* set );
+extern Bool VG_(isemptysigset) ( const vki_sigset_t* set );
extern Int VG_(sigaddset) ( vki_sigset_t* set, Int signum );
extern Int VG_(sigdelset) ( vki_sigset_t* set, Int signum );
-extern Int VG_(sigismember) ( vki_sigset_t* set, Int signum );
+extern Int VG_(sigismember) ( const vki_sigset_t* set, Int signum );
extern void VG_(sigaddset_from_set) ( vki_sigset_t* dst, vki_sigset_t* src );
--- valgrind/none/tests/Makefile.am #1.55:1.56
@@ -6,4 +6,5 @@
args.stderr.exp args.stdout.exp args.vgtest \
bitfield1.stderr.exp bitfield1.vgtest \
+ blockfault.vgtest \
closeall.stderr.exp closeall.vgtest \
cmdline1.stderr.exp cmdline1.stdout.exp cmdline1.vgtest \
@@ -58,5 +59,5 @@
check_PROGRAMS = \
- args bitfield1 closeall coolo_strlen \
+ args bitfield1 blockfault closeall coolo_strlen \
discard exec-sigmask execve fcntl_setown floored fork \
fucomip getseg \
@@ -75,4 +76,5 @@
args_SOURCES = args.c
bitfield1_SOURCES = bitfield1.c
+blockfault_SOURCES = blockfault.c
closeall_SOURCES = closeall.c
coolo_strlen_SOURCES = coolo_strlen.c
--- valgrind/coregrind/x86-linux/syscalls.c #1.8:1.9
@@ -399,4 +399,9 @@ PRE(sys_clone, Special)
cloneflags = arg1 & (VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES | VKI_CLONE_VFORK);
+ if ((arg1 & VKI_CSIGNAL) && !VG_(client_signal_OK)(arg1 & VKI_CSIGNAL)) {
+ set_result( -VKI_EINVAL );
+ return;
+ }
+
switch(cloneflags) {
case VKI_CLONE_VM | VKI_CLONE_FS | VKI_CLONE_FILES:
--- valgrind/coregrind/x86-linux/ldt.c #1.6:1.7
@@ -191,6 +191,11 @@ Addr VG_(do_useseg) ( UInt seg_selector,
privilege). This forms the bottom 2 bits of the selector. */
if ((seg_selector & 3) != 3) {
- VG_(synth_fault)(VG_(get_VCPU_tid)());
- return 0;
+ ThreadId tid = VG_(get_VCPU_tid)();
+ vg_assert(tid != VG_INVALID_THREADID);
+
+ //VG_(printf)("synth fault EIP=%p\n", VG_(baseBlock)[VGOFF_(m_eip)]);
+ VG_(synth_fault)(tid);
+ VG_(resume_scheduler)(tid);
+ VG_(core_panic)("do_useseg called outside of scheduler loop?");
}
--- valgrind/coregrind/linux/core_os.c #1.2:1.3
@@ -138,6 +138,4 @@ void VGA_(intercept_libc_freeres_wrapper
void VGA_(final_tidyup)(ThreadId tid)
{
- struct vki_sigaction sa;
-
vg_assert(VG_(is_running_thread)(tid));
@@ -161,12 +159,8 @@ void VGA_(final_tidyup)(ThreadId tid)
/* and restore handlers to default */
- sa.ksa_handler = VKI_SIG_DFL;
- VG_(sigemptyset)(&sa.sa_mask);
- sa.sa_flags = 0;
-
- VG_(do_sys_sigaction)(tid, VKI_SIGSEGV, &sa, NULL);
- VG_(do_sys_sigaction)(tid, VKI_SIGBUS, &sa, NULL);
- VG_(do_sys_sigaction)(tid, VKI_SIGILL, &sa, NULL);
- VG_(do_sys_sigaction)(tid, VKI_SIGFPE, &sa, NULL);
+ VG_(set_default_handler)(VKI_SIGSEGV);
+ VG_(set_default_handler)(VKI_SIGBUS);
+ VG_(set_default_handler)(VKI_SIGILL);
+ VG_(set_default_handler)(VKI_SIGFPE);
// We were exiting, so assert that...
--- valgrind/none/tests/x86/badseg.c #1.1:1.2
@@ -16,5 +16,5 @@ int main()
sa.sa_sigaction = handler;
- sigemptyset(&sa.sa_mask);
+ sigfillset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
|