Skip to content

Commit 12888df

Browse files
committed
Implement libas-safe, a library for extending AS-safety to all library functions
It does this either by redirecting the signal handler to a separate copy of the library (if libgotcha doesn't treat the symbols as whitelisted), or else by deferring handling of the signal. This was tested against 97410c8:posix_safety/as.c built with -fpic -O2 -D_DEFAULT_SOURCE and a vanilla build of libgotcha revision a211a7b with the following symlinks: * ./libgotcha/libgotcha.a -> .../libgotcha/libgotcha.a * ./libgotcha.h -> .../libgotcha/libgotcha_api.h * ./libgotcha.mk -> .../libgotcha/libgotcha.mk * ./libgotcha_repl.h -> .../libgotcha/libgotcha_repl.h
0 parents  commit 12888df

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/libgotcha.h
2+
/libgotcha.mk
3+
/libgotcha_repl.h
4+
5+
*.a
6+
*.o
7+
*.so

Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CFLAGS += -std=c99 -g -O2 -fpic -fno-optimize-sibling-calls -Wall -Wextra -Wpedantic
2+
CPPFLAGS += -D_DEFAULT_SOURCE
3+
4+
-include libgotcha.mk
5+
6+
.PHONY: all
7+
all: libas-safe.so
8+
9+
libas-safe.so: libgotcha.h
10+
11+
libgotcha.a: libgotcha/libgotcha.a
12+
objcopy -Wsigaction --globalize-symbol libgotcha_sigaction --globalize-symbol libgotcha_pthread_sigmask --globalize-symbol libgotcha_sigaddset --globalize-symbol libgotcha_sigfillset $< $@
13+
14+
.PHONY: clean
15+
clean:
16+
$(RM) *.a *.o *.so

as-safe.c

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include "libgotcha.h"
2+
#include "libgotcha_repl.h"
3+
4+
#include <assert.h>
5+
#include <signal.h>
6+
#include <stdbool.h>
7+
#include <stdio.h>
8+
#include <stdlib.h>
9+
#include <string.h>
10+
#include <threads.h>
11+
12+
struct handler {
13+
void (*handler)(int, siginfo_t *, void *);
14+
sigset_t mask;
15+
};
16+
17+
struct pending {
18+
siginfo_t info;
19+
sigset_t mask;
20+
};
21+
22+
static struct handler *handlers;
23+
static int (*mainfunc)(int, char **, char **);
24+
static libgotcha_group_t nonsignal;
25+
static thread_local struct pending pending;
26+
static bool verbose;
27+
28+
static int as_safe(int, char **, char **);
29+
#pragma weak libassafe_libc_start_main = __libc_start_main
30+
int __libc_start_main(int (*main)(int, char **, char **), int argc, char **argv, int (*init)(int, char **, char **), void (*fini)(void), void (*rtld_fini)(void), void *stack_end) {
31+
mainfunc = main;
32+
return __libc_start_main(as_safe, argc, argv, init, fini, rtld_fini, stack_end);
33+
}
34+
35+
static void restorer(void);
36+
static int as_safe(int argc, char **argv, char **envp) {
37+
if(getenv("LIBASSAFE_VERBOSE"))
38+
verbose = true;
39+
40+
if(verbose) fputs("LIBAS-SAFE: as_safe() initializating...\n", stderr);
41+
handlers = calloc(SIGRTMAX, sizeof *handlers);
42+
libgotcha_group_thread_set(nonsignal = libgotcha_group_new());
43+
libgotcha_shared_hook(restorer);
44+
return mainfunc(argc, argv, envp);
45+
}
46+
47+
static void stub(int, siginfo_t *, void *);
48+
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) {
49+
assert(signum >= 0);
50+
assert(signum < SIGRTMAX);
51+
52+
struct sigaction sa;
53+
struct handler backup;
54+
if(act) {
55+
if(verbose) fprintf(
56+
stderr,
57+
"LIBAS-SAFE: sigaction() installing new handler for signal %d\n",
58+
signum);
59+
60+
struct handler *record = handlers + signum;
61+
memcpy(&backup, record, sizeof backup);
62+
memcpy(&sa, act, sizeof sa);
63+
record->handler = sa.sa_sigaction;
64+
sa.sa_sigaction = stub;
65+
record->mask = sa.sa_mask;
66+
libgotcha_sigfillset(&sa.sa_mask);
67+
68+
sa.sa_flags |= SA_SIGINFO;
69+
if(sa.sa_flags & SA_NODEFER) {
70+
sigdelset(&record->mask, signum);
71+
sa.sa_flags &= ~SA_NODEFER;
72+
} else
73+
libgotcha_sigaddset(&record->mask, signum);
74+
75+
act = &sa;
76+
}
77+
78+
int status = libgotcha_sigaction(signum, act, oldact);
79+
if(status)
80+
memcpy(handlers + signum, &backup, sizeof backup);
81+
if(oldact && oldact->sa_sigaction == stub)
82+
oldact->sa_sigaction = handlers[signum].handler;
83+
return status;
84+
}
85+
86+
static void stub(int no, siginfo_t *si, void *co) {
87+
libgotcha_group_t group = libgotcha_group_thread_get();
88+
siginfo_t extra = {
89+
.si_signo = 0,
90+
};
91+
if(pending.info.si_signo) {
92+
assert(pending.info.si_signo == no);
93+
94+
ucontext_t *uc = co;
95+
memcpy(&extra, &pending.info, sizeof extra);
96+
memcpy(&uc->uc_sigmask, &pending.mask, sizeof uc->uc_sigmask);
97+
pending.info.si_signo = 0;
98+
} else if(group == LIBGOTCHA_GROUP_SHARED) {
99+
if(verbose) fprintf(
100+
stderr,
101+
"LIBAS-SAFE: stub() deferring handling of signal %d to after shared code\n",
102+
no);
103+
104+
ucontext_t *uc = co;
105+
memcpy(&pending.info, si, sizeof pending.info);
106+
memcpy(&pending.mask, &uc->uc_sigmask, sizeof pending.mask);
107+
libgotcha_sigfillset(&uc->uc_sigmask);
108+
return;
109+
}
110+
111+
struct handler *record = handlers + no;
112+
libgotcha_pthread_sigmask(SIG_SETMASK, &record->mask, NULL);
113+
libgotcha_group_thread_set(LIBGOTCHA_GROUP_SHARED);
114+
if(extra.si_signo)
115+
record->handler(no, &extra, co);
116+
record->handler(no, si, co);
117+
libgotcha_group_thread_set(group);
118+
}
119+
120+
static void restorer(void) {
121+
if(pending.info.si_signo) {
122+
sigset_t full;
123+
libgotcha_sigfillset(&full);
124+
sigdelset(&full, pending.info.si_signo);
125+
sigsuspend(&full);
126+
}
127+
}

0 commit comments

Comments
 (0)