Skip to content

Commit a327c7d

Browse files
committed
Eliminate cyclic locks in runtime
This change introduces a new deadlock detector for Cosmo's POSIX threads implementation. Error check mutexes will now track a DAG of nested locks and report EDEADLK when a deadlock is theoretically possible. These will occur rarely, but it's important for production hardening your code. You don't even need to change your mutexes to use the POSIX error check mode because `cosmocc -mdbg` will enable error checking on mutexes by default globally. When cycles are found, an error message showing your demangled symbols describing the strongly connected component are printed and then the SIGTRAP is raised, which means you'll also get a backtrace if you're using ShowCrashReports() too. This new error checker is so low-level and so pure that it's able to verify the relationships of every libc runtime lock, including those locks upon which the mutex implementation depends.
1 parent 26c051c commit a327c7d

File tree

143 files changed

+2122
-1620
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

143 files changed

+2122
-1620
lines changed

libc/calls/getloadavg-nt.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,23 @@
2121
#include "libc/calls/syscall_support-nt.internal.h"
2222
#include "libc/dce.h"
2323
#include "libc/fmt/conv.h"
24+
#include "libc/intrin/cxaatexit.h"
2425
#include "libc/macros.h"
2526
#include "libc/nt/accounting.h"
2627
#include "libc/runtime/runtime.h"
27-
#include "libc/thread/thread.h"
2828

29+
#define CTOR __attribute__((__constructor__(99)))
2930
#define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32)
3031

3132
static int cpus;
3233
static double load;
33-
static pthread_spinlock_t lock;
3434
static struct NtFileTime idle1, kern1, user1;
3535

3636
textwindows int sys_getloadavg_nt(double *a, int n) {
3737
int i, rc;
3838
uint64_t elapsed, used;
3939
struct NtFileTime idle, kern, user;
40-
BLOCK_SIGNALS;
41-
pthread_spin_lock(&lock);
40+
__cxa_lock();
4241
if (GetSystemTimes(&idle, &kern, &user)) {
4342
elapsed = (FT(kern) - FT(kern1)) + (FT(user) - FT(user1));
4443
if (elapsed) {
@@ -54,12 +53,11 @@ textwindows int sys_getloadavg_nt(double *a, int n) {
5453
} else {
5554
rc = __winerr();
5655
}
57-
pthread_spin_unlock(&lock);
58-
ALLOW_SIGNALS;
56+
__cxa_unlock();
5957
return rc;
6058
}
6159

62-
__attribute__((__constructor__(40))) static textstartup void ntinitload(void) {
60+
CTOR static textstartup void sys_getloadavg_nt_init(void) {
6361
if (IsWindows()) {
6462
load = 1;
6563
cpus = __get_cpu_count() / 2;

libc/calls/getprogramexecutablename.greg.c

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,8 @@ static int OldApeLoader(char *s) {
9696
static int CopyWithCwd(const char *q, char *p, char *e) {
9797
char c;
9898
if (*q != '/') {
99-
if (q[0] == '.' && q[1] == '/') {
99+
if (q[0] == '.' && q[1] == '/')
100100
q += 2;
101-
}
102101
int got = __getcwd(p, e - p - 1 /* '/' */);
103102
if (got != -1) {
104103
p += got - 1;
@@ -118,9 +117,10 @@ static int CopyWithCwd(const char *q, char *p, char *e) {
118117

119118
// if q exists then turn it into an absolute path.
120119
static int TryPath(const char *q) {
121-
if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf))) {
120+
if (!q)
121+
return 0;
122+
if (!CopyWithCwd(q, g_prog.u.buf, g_prog.u.buf + sizeof(g_prog.u.buf)))
122123
return 0;
123-
}
124124
return !sys_faccessat(AT_FDCWD, g_prog.u.buf, F_OK, 0);
125125
}
126126

@@ -129,9 +129,8 @@ static int TryPath(const char *q) {
129129
void __init_program_executable_name(void) {
130130
if (__program_executable_name && *__program_executable_name != '/' &&
131131
CopyWithCwd(__program_executable_name, g_prog.u.buf,
132-
g_prog.u.buf + sizeof(g_prog.u.buf))) {
132+
g_prog.u.buf + sizeof(g_prog.u.buf)))
133133
__program_executable_name = g_prog.u.buf;
134-
}
135134
}
136135

137136
static inline void InitProgramExecutableNameImpl(void) {
@@ -212,14 +211,12 @@ static inline void InitProgramExecutableNameImpl(void) {
212211
}
213212

214213
// don't trust argv or envp if set-id.
215-
if (issetugid()) {
214+
if (issetugid())
216215
goto UseEmpty;
217-
}
218216

219217
// try argv[0], then then $_.
220-
if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s)) {
218+
if (TryPath(__argv[0]) || TryPath(__getenv(__envp, "_").s))
221219
goto UseBuf;
222-
}
223220

224221
// give up and just copy argv[0] into it
225222
if ((q = __argv[0])) {

libc/calls/state.internal.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ extern unsigned __sighandflags[NSIG + 1];
1313
extern uint64_t __sighandmask[NSIG + 1];
1414
extern const struct NtSecurityAttributes kNtIsInheritable;
1515

16-
void __fds_wipe(void);
1716
void __fds_lock(void);
1817
void __fds_unlock(void);
1918

libc/calls/struct/sigset.internal.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,15 @@
55
#include "libc/sysv/consts/sig.h"
66
COSMOPOLITAN_C_START_
77

8-
#ifndef MODE_DBG
9-
/* block sigs because theoretical edge cases */
108
#define BLOCK_SIGNALS \
119
do { \
1210
sigset_t _SigMask; \
1311
_SigMask = __sig_block()
12+
1413
#define ALLOW_SIGNALS \
1514
__sig_unblock(_SigMask); \
1615
} \
1716
while (0)
18-
#else
19-
/* doesn't block signals so we can get a crash
20-
report, when a core runtime library crashes */
21-
#define BLOCK_SIGNALS \
22-
do { \
23-
sigset_t _SigMask; \
24-
sigprocmask(SIG_SETMASK, 0, &_SigMask)
25-
#define ALLOW_SIGNALS \
26-
} \
27-
while (0)
28-
#endif
2917

3018
sigset_t __sig_block(void);
3119
void __sig_unblock(sigset_t);

libc/cosmo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,11 @@ int cosmo_futex_wake(_COSMO_ATOMIC(int) *, int, char);
2222
int cosmo_futex_wait(_COSMO_ATOMIC(int) *, int, char, int,
2323
const struct timespec *);
2424

25+
int __deadlock_check(void *, int) libcesque;
26+
int __deadlock_tracked(void *) libcesque;
27+
void __deadlock_record(void *, int) libcesque;
28+
void __deadlock_track(void *, int) libcesque;
29+
void __deadlock_untrack(void *) libcesque;
30+
2531
COSMOPOLITAN_C_END_
2632
#endif /* COSMOPOLITAN_LIBC_COSMO_H_ */

libc/errno.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ COSMOPOLITAN_C_START_
2626
/* this header is included by 700+ files; therefore we */
2727
/* hand-roll &__get_tls()->tib_errno to avoid #include */
2828
/* cosmopolitan uses x28 as the tls register b/c apple */
29-
#define errno \
30-
(*__extension__({ \
31-
errno_t *__ep; \
32-
__asm__("sub\t%0,x28,#512-0x3c" : "=r"(__ep)); \
33-
__ep; \
29+
#define errno \
30+
(*__extension__({ \
31+
errno_t *__ep; \
32+
__asm__("sub\t%0,x28,#1024-0x3c" : "=r"(__ep)); \
33+
__ep; \
3434
}))
3535
#else
3636
#define errno (*__errno_location())

libc/integral/c.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ typedef struct {
135135
#define strftimeesque(n) __attribute__((__format__(__strftime__, n, 0)))
136136

137137
#ifndef privileged
138-
#define privileged _Section(".privileged") dontinline dontinstrument dontubsan
138+
#define privileged _Section(".privileged") dontinstrument dontubsan
139139
#endif
140140

141141
#ifndef wontreturn

libc/intrin/__getenv.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
#include "libc/intrin/getenv.h"
2121
#include "libc/intrin/kprintf.h"
2222

23-
privileged struct Env __getenv(char **p, const char *k) {
23+
privileged optimizesize struct Env __getenv(char **p, const char *k) {
2424
char *t;
2525
int i, j;
2626
for (i = 0; (t = p[i]); ++i) {

libc/intrin/bzero.c

Lines changed: 1 addition & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -16,155 +16,18 @@
1616
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
19-
#include "libc/assert.h"
20-
#include "libc/dce.h"
21-
#include "libc/nexgen32e/nexgen32e.h"
22-
#include "libc/nexgen32e/x86feature.h"
2319
#include "libc/str/str.h"
2420

25-
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(1)));
26-
typedef long long xmm_a __attribute__((__vector_size__(16), __aligned__(16)));
27-
28-
static void bzero128(char *p, size_t n) {
29-
xmm_t v = {0};
30-
if (n <= 32) {
31-
*(xmm_t *)(p + n - 16) = v;
32-
*(xmm_t *)p = v;
33-
} else {
34-
do {
35-
n -= 32;
36-
*(xmm_t *)(p + n) = v;
37-
*(xmm_t *)(p + n + 16) = v;
38-
} while (n > 32);
39-
*(xmm_t *)(p + 16) = v;
40-
*(xmm_t *)p = v;
41-
}
42-
}
43-
44-
#if defined(__x86_64__) && !defined(__chibicc__)
45-
_Microarchitecture("avx") static void bzero_avx(char *p, size_t n) {
46-
xmm_t v = {0};
47-
if (n <= 32) {
48-
*(xmm_t *)(p + n - 16) = v;
49-
*(xmm_t *)p = v;
50-
} else if (n >= 1024 && X86_HAVE(ERMS)) {
51-
asm("rep stosb" : "+D"(p), "+c"(n), "=m"(*(char(*)[n])p) : "a"(0));
52-
} else {
53-
if (n < kHalfCache3 || !kHalfCache3) {
54-
do {
55-
n -= 32;
56-
*(xmm_t *)(p + n) = v;
57-
*(xmm_t *)(p + n + 16) = v;
58-
} while (n > 32);
59-
} else {
60-
while ((uintptr_t)(p + n) & 15) {
61-
p[--n] = 0;
62-
}
63-
do {
64-
n -= 32;
65-
__builtin_ia32_movntdq((xmm_a *)(p + n), (xmm_a)v);
66-
__builtin_ia32_movntdq((xmm_a *)(p + n + 16), (xmm_a)v);
67-
} while (n > 32);
68-
asm("sfence");
69-
}
70-
*(xmm_t *)(p + 16) = v;
71-
*(xmm_t *)p = v;
72-
}
73-
}
74-
#endif
75-
7621
/**
7722
* Sets memory to zero.
7823
*
79-
* bzero n=0 661 picoseconds
80-
* bzero n=1 661 ps/byte 1,476 mb/s
81-
* bzero n=2 330 ps/byte 2,952 mb/s
82-
* bzero n=3 220 ps/byte 4,428 mb/s
83-
* bzero n=4 165 ps/byte 5,904 mb/s
84-
* bzero n=7 94 ps/byte 10,333 mb/s
85-
* bzero n=8 41 ps/byte 23,618 mb/s
86-
* bzero n=15 44 ps/byte 22,142 mb/s
87-
* bzero n=16 20 ps/byte 47,236 mb/s
88-
* bzero n=31 21 ps/byte 45,760 mb/s
89-
* bzero n=32 20 ps/byte 47,236 mb/s
90-
* bzero n=63 10 ps/byte 92,997 mb/s
91-
* bzero n=64 15 ps/byte 62,982 mb/s
92-
* bzero n=127 15 ps/byte 62,490 mb/s
93-
* bzero n=128 10 ps/byte 94,473 mb/s
94-
* bzero n=255 14 ps/byte 68,439 mb/s
95-
* bzero n=256 9 ps/byte 105 gb/s
96-
* bzero n=511 15 ps/byte 62,859 mb/s
97-
* bzero n=512 11 ps/byte 83,976 mb/s
98-
* bzero n=1023 15 ps/byte 61,636 mb/s
99-
* bzero n=1024 10 ps/byte 88,916 mb/s
100-
* bzero n=2047 9 ps/byte 105 gb/s
101-
* bzero n=2048 8 ps/byte 109 gb/s
102-
* bzero n=4095 8 ps/byte 115 gb/s
103-
* bzero n=4096 8 ps/byte 118 gb/s
104-
* bzero n=8191 7 ps/byte 129 gb/s
105-
* bzero n=8192 7 ps/byte 130 gb/s
106-
* bzero n=16383 6 ps/byte 136 gb/s
107-
* bzero n=16384 6 ps/byte 137 gb/s
108-
* bzero n=32767 6 ps/byte 140 gb/s
109-
* bzero n=32768 6 ps/byte 141 gb/s
110-
* bzero n=65535 15 ps/byte 64,257 mb/s
111-
* bzero n=65536 15 ps/byte 64,279 mb/s
112-
* bzero n=131071 15 ps/byte 63,166 mb/s
113-
* bzero n=131072 15 ps/byte 63,115 mb/s
114-
* bzero n=262143 15 ps/byte 62,052 mb/s
115-
* bzero n=262144 15 ps/byte 62,097 mb/s
116-
* bzero n=524287 15 ps/byte 61,699 mb/s
117-
* bzero n=524288 15 ps/byte 61,674 mb/s
118-
* bzero n=1048575 16 ps/byte 60,179 mb/s
119-
* bzero n=1048576 15 ps/byte 61,330 mb/s
120-
* bzero n=2097151 15 ps/byte 61,071 mb/s
121-
* bzero n=2097152 15 ps/byte 61,065 mb/s
122-
* bzero n=4194303 16 ps/byte 60,942 mb/s
123-
* bzero n=4194304 16 ps/byte 60,947 mb/s
124-
* bzero n=8388607 16 ps/byte 60,872 mb/s
125-
* bzero n=8388608 16 ps/byte 60,879 mb/s
126-
*
12724
* @param p is memory address
12825
* @param n is byte length
12926
* @return p
13027
* @asyncsignalsafe
13128
*/
13229
void bzero(void *p, size_t n) {
133-
char *b;
134-
uint64_t x;
135-
b = p;
136-
#ifdef __x86_64__
137-
asm("xorl\t%k0,%k0" : "=r"(x));
138-
#else
139-
if (1) {
140-
memset(p, 0, n);
141-
return;
142-
}
143-
x = 0;
144-
#endif
145-
if (n <= 16) {
146-
if (n >= 8) {
147-
__builtin_memcpy(b, &x, 8);
148-
__builtin_memcpy(b + n - 8, &x, 8);
149-
} else if (n >= 4) {
150-
__builtin_memcpy(b, &x, 4);
151-
__builtin_memcpy(b + n - 4, &x, 4);
152-
} else if (n) {
153-
do {
154-
asm volatile("" ::: "memory");
155-
b[--n] = x;
156-
} while (n);
157-
}
158-
#if defined(__x86_64__) && !defined(__chibicc__)
159-
} else if (IsTiny()) {
160-
asm("rep stosb" : "+D"(b), "+c"(n), "=m"(*(char(*)[n])b) : "a"(0));
161-
return;
162-
} else if (X86_HAVE(AVX)) {
163-
bzero_avx(b, n);
164-
#endif
165-
} else {
166-
bzero128(b, n);
167-
}
30+
memset(p, 0, n);
16831
}
16932

17033
__weak_reference(bzero, explicit_bzero);

libc/intrin/cxalock.c

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,7 @@
1919
#include "libc/intrin/cxaatexit.h"
2020
#include "libc/thread/thread.h"
2121

22-
static pthread_mutex_t __cxa_lock_obj;
23-
24-
void __cxa_wipe(void) {
25-
pthread_mutex_init(&__cxa_lock_obj, 0);
26-
}
22+
pthread_mutex_t __cxa_lock_obj = PTHREAD_MUTEX_INITIALIZER;
2723

2824
void __cxa_lock(void) {
2925
pthread_mutex_lock(&__cxa_lock_obj);
@@ -32,7 +28,3 @@ void __cxa_lock(void) {
3228
void __cxa_unlock(void) {
3329
pthread_mutex_unlock(&__cxa_lock_obj);
3430
}
35-
36-
__attribute__((__constructor__(60))) static textstartup void __cxa_init() {
37-
pthread_atfork(__cxa_lock, __cxa_unlock, __cxa_wipe);
38-
}

0 commit comments

Comments
 (0)