Skip to content

Commit 820d36a

Browse files
authored
Merge pull request #163 from tornaria/signal-safety
Signal safety
2 parents 6d47418 + ee1a145 commit 820d36a

File tree

2 files changed

+86
-40
lines changed

2 files changed

+86
-40
lines changed

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ CXX="$CC"
2727
CXXFLAGS="$CFLAGS"
2828
AC_PROG_CXX()
2929

30-
AC_CHECK_HEADERS([execinfo.h sys/mman.h sys/prctl.h sys/time.h sys/wait.h windows.h])
30+
AC_CHECK_HEADERS([execinfo.h sys/mman.h sys/prctl.h time.h sys/wait.h windows.h])
3131
AC_CHECK_FUNCS([fork kill sigprocmask sigaltstack backtrace])
3232

3333

src/cysignals/implementation.c

Lines changed: 85 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ Interrupt and signal handling for Cython
3838
#if HAVE_SYS_TYPES_H
3939
#include <sys/types.h>
4040
#endif
41-
#if HAVE_SYS_TIME_H
42-
#include <sys/time.h>
41+
#if HAVE_TIME_H
42+
#include <time.h>
4343
#endif
4444
#if HAVE_SYS_WAIT_H
4545
#include <sys/wait.h>
@@ -102,7 +102,7 @@ void custom_set_pending_signal(int sig){
102102

103103

104104
#if ENABLE_DEBUG_CYSIGNALS
105-
static struct timeval sigtime; /* Time of signal */
105+
static struct timespec sigtime; /* Time of signal */
106106
#endif
107107

108108
/* The cysigs object (there is a unique copy of this, shared by all
@@ -155,6 +155,52 @@ static inline void reset_CPU(void)
155155
#endif
156156
}
157157

158+
static inline void print_stderr(const char* s)
159+
{
160+
/* Using stdio (fputs, fprintf, fflush) from inside a signal
161+
* handler is undefined, see signal-safety(7). We use write(2)
162+
* instead, which is async-signal-safe according to POSIX. */
163+
write(2, s, strlen(s));
164+
}
165+
166+
/* str should have enough space allocated */
167+
static inline void ulong_to_str(unsigned long val, char *str, int base)
168+
{
169+
const char xdigits[16] = "0123456789abcdef";
170+
unsigned long aux;
171+
int len;
172+
173+
len = 1; aux = val;
174+
while (aux /= base) len++;
175+
176+
str += len; *str = 0;
177+
do *--str = xdigits[val % base]; while (val /= base);
178+
}
179+
180+
static inline void long_to_str(long val, char *str, int base)
181+
{
182+
if (val < 0) *str++ = '-';
183+
ulong_to_str(val < 0 ? -val : val, str, base);
184+
}
185+
186+
static inline void print_stderr_long(long val)
187+
{
188+
char buf[21];
189+
long_to_str(val, buf, 10);
190+
print_stderr(buf);
191+
}
192+
193+
static inline void print_stderr_ptr(void *ptr)
194+
{
195+
if (!ptr)
196+
print_stderr("(nil)");
197+
else {
198+
char buf[17];
199+
ulong_to_str((unsigned long)ptr, buf, 16);
200+
print_stderr("0x");
201+
print_stderr(buf);
202+
}
203+
}
158204

159205
/* Reset all signal handlers and the signal mask to their defaults. */
160206
static inline void sig_reset_defaults(void) {
@@ -234,12 +280,16 @@ static void cysigs_interrupt_handler(int sig)
234280
{
235281
#if ENABLE_DEBUG_CYSIGNALS
236282
if (cysigs.debug_level >= 1) {
237-
fprintf(stderr, "\n*** SIG %i *** %s sig_on\n", sig, (cysigs.sig_on_count > 0) ? "inside" : "outside");
283+
print_stderr("\n*** SIG ");
284+
print_stderr_long(sig);
285+
if (cysigs.sig_on_count > 0)
286+
print_stderr(" *** inside sig_on\n");
287+
else
288+
print_stderr(" *** outside sig_on\n");
238289
if (cysigs.debug_level >= 3) print_backtrace();
239-
fflush(stderr);
240290
/* Store time of this signal, unless there is already a
241291
* pending signal. */
242-
if (!cysigs.interrupt_received) gettimeofday(&sigtime, NULL);
292+
if (!cysigs.interrupt_received) clock_gettime(CLOCK_MONOTONIC, &sigtime);
243293
}
244294
#endif
245295

@@ -288,10 +338,11 @@ static void cysigs_signal_handler(int sig)
288338
/* We are inside sig_on(), so we can handle the signal! */
289339
#if ENABLE_DEBUG_CYSIGNALS
290340
if (cysigs.debug_level >= 1) {
291-
fprintf(stderr, "\n*** SIG %i *** inside sig_on\n", sig);
341+
print_stderr("\n*** SIG ");
342+
print_stderr_long(sig);
343+
print_stderr(" *** inside sig_on\n");
292344
if (cysigs.debug_level >= 3) print_backtrace();
293-
fflush(stderr);
294-
gettimeofday(&sigtime, NULL);
345+
clock_gettime(CLOCK_MONOTONIC, &sigtime);
295346
}
296347
#endif
297348

@@ -401,15 +452,19 @@ static void setup_trampoline(void)
401452
static void do_raise_exception(int sig)
402453
{
403454
#if ENABLE_DEBUG_CYSIGNALS
404-
struct timeval raisetime;
455+
struct timespec raisetime;
405456
if (cysigs.debug_level >= 2) {
406-
gettimeofday(&raisetime, NULL);
407-
long delta_ms = (raisetime.tv_sec - sigtime.tv_sec)*1000L + ((long)raisetime.tv_usec - (long)sigtime.tv_usec)/1000;
457+
clock_gettime(CLOCK_MONOTONIC, &raisetime);
458+
long delta_ms = (raisetime.tv_sec - sigtime.tv_sec)*1000L + (raisetime.tv_nsec - sigtime.tv_nsec)/1000000L;
408459
PyGILState_STATE gilstate = PyGILState_Ensure();
409-
fprintf(stderr, "do_raise_exception(sig=%i)\nPyErr_Occurred() = %p\nRaising Python exception %li ms after signal...\n",
410-
sig, PyErr_Occurred(), delta_ms);
460+
print_stderr("do_raise_exception(sig=");
461+
print_stderr_long(sig);
462+
print_stderr(")\nPyErr_Occurred() = ");
463+
print_stderr_ptr(PyErr_Occurred());
464+
print_stderr("\nRaising Python exception ");
465+
print_stderr_long(delta_ms);
466+
print_stderr("ms after signal...\n");
411467
PyGILState_Release(gilstate);
412-
fflush(stderr);
413468
}
414469
#endif
415470

@@ -550,39 +605,32 @@ static void setup_cysignals_handlers(void)
550605

551606
static void print_sep(void)
552607
{
553-
fputs("------------------------------------------------------------------------\n",
554-
stderr);
555-
fflush(stderr);
608+
print_stderr("------------------------------------------------------------------------\n");
556609
}
557610

558611
/* Print a backtrace if supported by libc */
559612
static void print_backtrace()
560613
{
561-
fflush(stderr);
562614
#if HAVE_BACKTRACE
563615
void* backtracebuffer[BACKTRACELEN];
564616
int btsize = backtrace(backtracebuffer, BACKTRACELEN);
565617
if (btsize)
566618
backtrace_symbols_fd(backtracebuffer, btsize, 2);
567619
else
568-
fputs("(no backtrace available)\n", stderr);
620+
print_stderr("(no backtrace available)\n");
569621
print_sep();
570622
#endif
571623
}
572624

573625
/* Print a backtrace using gdb */
574-
static void print_enhanced_backtrace(void)
626+
static inline void print_enhanced_backtrace(void)
575627
{
576628
/* Bypass Linux Yama restrictions on ptrace() to allow debugging */
577629
/* See https://www.kernel.org/doc/Documentation/security/Yama.txt */
578630
#ifdef PR_SET_PTRACER
579631
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
580632
#endif
581633

582-
/* Flush all buffers before forking */
583-
fflush(stdout);
584-
fflush(stderr);
585-
586634
/* Enhanced backtraces are only supported on POSIX systems */
587635
#if HAVE_FORK
588636
pid_t parent_pid = getpid();
@@ -591,7 +639,9 @@ static void print_enhanced_backtrace(void)
591639
if (pid < 0)
592640
{
593641
/* Failed to fork: no problem, just ignore */
594-
perror("cysignals fork");
642+
print_stderr("cysignals fork: ");
643+
print_stderr(strerror(errno));
644+
print_stderr("\n");
595645
return;
596646
}
597647

@@ -601,20 +651,21 @@ static void print_enhanced_backtrace(void)
601651

602652
/* We deliberately put these variables on the stack to avoid
603653
* malloc() calls, the heap might be messed up! */
604-
char path[1024];
654+
char* path = "cysignals-CSI";
605655
char pid_str[32];
606656
char* argv[5];
607657

608-
snprintf(path, sizeof(path), "cysignals-CSI");
609-
snprintf(pid_str, sizeof(pid_str), "%i", parent_pid);
658+
long_to_str(parent_pid, pid_str, 10);
610659

611660
argv[0] = "cysignals-CSI";
612661
argv[1] = "--no-color";
613662
argv[2] = "--pid";
614663
argv[3] = pid_str;
615664
argv[4] = NULL;
616665
execvp(path, argv);
617-
perror("cysignals failed to execute cysignals-CSI");
666+
print_stderr("cysignals failed to execute cysignals-CSI: ");
667+
print_stderr(strerror(errno));
668+
print_stderr("\n");
618669
exit(2);
619670
}
620671
/* Wait for cysignals-CSI to finish */
@@ -649,17 +700,12 @@ static void sigdie(int sig, const char* s)
649700
#endif
650701

651702
if (s) {
652-
/* Using fprintf from inside a signal handler is undefined,
653-
see signal-safety(7). We use write(2) instead, which is
654-
async-signal-safe according to POSIX. */
655-
const char * message =
656-
"\n"
703+
print_stderr(s);
704+
print_stderr("\n"
657705
"This probably occurred because a *compiled* module has a bug\n"
658706
"in it and is not properly wrapped with sig_on(), sig_off().\n"
659-
"Python will now terminate.\n"
660-
"------------------------------------------------------------------------\n";
661-
write(2, s, strlen(s));
662-
write(2, message, strlen(message));
707+
"Python will now terminate.\n");
708+
print_sep();
663709
}
664710

665711
dienow:

0 commit comments

Comments
 (0)