xref: /illumos-gate/usr/src/lib/libc/port/threads/assfail.c (revision 7f9dc0eec05dc1280e3a494124572c5814e7aef9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Copyright (c) 2012, 2014 by Delphix. All rights reserved.
28  * Copyright 2015 Joyent, Inc.
29  * Copyright 2020 Oxide Computer Company
30  */
31 
32 #include "lint.h"
33 #include "libc.h"
34 #include "thr_uberdata.h"
35 #include <upanic.h>
36 
37 const char *panicstr;
38 ulwp_t *panic_thread;
39 
40 static mutex_t assert_lock = DEFAULTMUTEX;
41 static ulwp_t *assert_thread = NULL;
42 
43 mutex_t *panic_mutex = NULL;
44 
45 static void Abort(const char *, size_t) __NORETURN;
46 
47 /*
48  * Called from __assert() to set panicstr and panic_thread.
49  */
50 void
__set_panicstr(const char * msg)51 __set_panicstr(const char *msg)
52 {
53 	panicstr = msg;
54 	panic_thread = __curthread();
55 }
56 
57 /*
58  * Called from exit() (atexit function) to give precedence
59  * to assertion failures and a core dump over _exit().
60  */
61 void
grab_assert_lock()62 grab_assert_lock()
63 {
64 	(void) _lwp_mutex_lock(&assert_lock);
65 }
66 
67 static void
Abort(const char * msg,size_t buflen)68 Abort(const char *msg, size_t buflen)
69 {
70 	ulwp_t *self;
71 
72 	/* to help with core file debugging */
73 	panicstr = msg;
74 	if ((self = __curthread()) != NULL) {
75 		panic_thread = self;
76 	}
77 
78 	upanic(msg, buflen);
79 }
80 
81 /*
82  * Write a panic message w/o grabbing any locks other than assert_lock.
83  * We have no idea what locks are held at this point.
84  */
85 void
common_panic(const char * head,const char * why)86 common_panic(const char *head, const char *why)
87 {
88 	char msg[400];	/* no panic() message in the library is this long */
89 	ulwp_t *self;
90 	size_t len1, len2;
91 
92 	if ((self = __curthread()) != NULL)
93 		enter_critical(self);
94 	(void) _lwp_mutex_lock(&assert_lock);
95 
96 	(void) memset(msg, 0, sizeof (msg));
97 	(void) strcpy(msg, head);
98 	len1 = strlen(msg);
99 	len2 = strlen(why);
100 	if (len1 + len2 >= sizeof (msg))
101 		len2 = sizeof (msg) - len1 - 1;
102 	(void) strncat(msg, why, len2);
103 	len1 = strlen(msg);
104 	if (msg[len1 - 1] != '\n')
105 		msg[len1++] = '\n';
106 	(void) __write(2, msg, len1);
107 	Abort(msg, sizeof (msg));
108 }
109 
110 void
thr_panic(const char * why)111 thr_panic(const char *why)
112 {
113 	common_panic("*** libc thread failure: ", why);
114 }
115 
116 void
aio_panic(const char * why)117 aio_panic(const char *why)
118 {
119 	common_panic("*** libc aio system failure: ", why);
120 }
121 
122 void
mutex_panic(mutex_t * mp,const char * why)123 mutex_panic(mutex_t *mp, const char *why)
124 {
125 	panic_mutex = mp;
126 	common_panic("*** libc mutex system failure: ", why);
127 }
128 
129 /*
130  * Utility function for converting a long integer to a string, avoiding stdio.
131  * 'base' must be one of 10 or 16
132  */
133 void
ultos(uint64_t n,int base,char * s)134 ultos(uint64_t n, int base, char *s)
135 {
136 	char lbuf[24];		/* 64 bits fits in 16 hex digits, 20 decimal */
137 	char *cp = lbuf;
138 
139 	do {
140 		*cp++ = "0123456789abcdef"[n%base];
141 		n /= base;
142 	} while (n);
143 	if (base == 16) {
144 		*s++ = '0';
145 		*s++ = 'x';
146 	}
147 	do {
148 		*s++ = *--cp;
149 	} while (cp > lbuf);
150 	*s = '\0';
151 }
152 
153 /*
154  * Report application lock usage error for mutexes and condvars.
155  * Not called if _THREAD_ERROR_DETECTION=0.
156  * Continue execution if _THREAD_ERROR_DETECTION=1.
157  * Dump core if _THREAD_ERROR_DETECTION=2.
158  */
159 void
lock_error(const mutex_t * mp,const char * who,void * cv,const char * msg)160 lock_error(const mutex_t *mp, const char *who, void *cv, const char *msg)
161 {
162 	mutex_t mcopy;
163 	char buf[800];
164 	uberdata_t *udp;
165 	ulwp_t *self;
166 	lwpid_t lwpid;
167 	pid_t pid;
168 
169 	/*
170 	 * Take a snapshot of the mutex before it changes (we hope!).
171 	 * Use memcpy() rather than 'mcopy = *mp' in case mp is unaligned.
172 	 */
173 	(void) memcpy(&mcopy, mp, sizeof (mcopy));
174 
175 	/* avoid recursion deadlock */
176 	if ((self = __curthread()) != NULL) {
177 		if (assert_thread == self)
178 			_exit(127);
179 		enter_critical(self);
180 		(void) _lwp_mutex_lock(&assert_lock);
181 		assert_thread = self;
182 		lwpid = self->ul_lwpid;
183 		udp = self->ul_uberdata;
184 		pid = udp->pid;
185 	} else {
186 		self = NULL;
187 		(void) _lwp_mutex_lock(&assert_lock);
188 		lwpid = _lwp_self();
189 		udp = &__uberdata;
190 		pid = getpid();
191 	}
192 
193 	(void) strcpy(buf,
194 	    "\n*** _THREAD_ERROR_DETECTION: lock usage error detected ***\n");
195 	(void) strcat(buf, who);
196 	(void) strcat(buf, "(");
197 	if (cv != NULL) {
198 		ultos((uint64_t)(uintptr_t)cv, 16, buf + strlen(buf));
199 		(void) strcat(buf, ", ");
200 	}
201 	ultos((uint64_t)(uintptr_t)mp, 16, buf + strlen(buf));
202 	(void) strcat(buf, ")");
203 	if (msg != NULL) {
204 		(void) strcat(buf, ": ");
205 		(void) strcat(buf, msg);
206 	} else if (!mutex_held(&mcopy)) {
207 		(void) strcat(buf, ": calling thread does not own the lock");
208 	} else if (mcopy.mutex_rcount) {
209 		(void) strcat(buf, ": mutex rcount = ");
210 		ultos((uint64_t)mcopy.mutex_rcount, 10, buf + strlen(buf));
211 	} else {
212 		(void) strcat(buf, ": calling thread already owns the lock");
213 	}
214 	(void) strcat(buf, "\ncalling thread is ");
215 	ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
216 	(void) strcat(buf, " thread-id ");
217 	ultos((uint64_t)lwpid, 10, buf + strlen(buf));
218 	if (msg != NULL || mutex_held(&mcopy))
219 		/* EMPTY */;
220 	else if (mcopy.mutex_lockw == 0)
221 		(void) strcat(buf, "\nthe lock is unowned");
222 	else if (!(mcopy.mutex_type & USYNC_PROCESS)) {
223 		(void) strcat(buf, "\nthe lock owner is ");
224 		ultos((uint64_t)mcopy.mutex_owner, 16, buf + strlen(buf));
225 	} else {
226 		(void) strcat(buf, " in process ");
227 		ultos((uint64_t)pid, 10, buf + strlen(buf));
228 		(void) strcat(buf, "\nthe lock owner is ");
229 		ultos((uint64_t)mcopy.mutex_owner, 16, buf + strlen(buf));
230 		(void) strcat(buf, " in process ");
231 		ultos((uint64_t)mcopy.mutex_ownerpid, 10, buf + strlen(buf));
232 	}
233 	(void) strcat(buf, "\n\n");
234 	(void) __write(2, buf, strlen(buf));
235 	if (udp->uberflags.uf_thread_error_detection >= 2)
236 		Abort(buf, sizeof (buf));
237 	assert_thread = NULL;
238 	(void) _lwp_mutex_unlock(&assert_lock);
239 	if (self != NULL)
240 		exit_critical(self);
241 }
242 
243 /*
244  * Report application lock usage error for rwlocks.
245  * Not called if _THREAD_ERROR_DETECTION=0.
246  * Continue execution if _THREAD_ERROR_DETECTION=1.
247  * Dump core if _THREAD_ERROR_DETECTION=2.
248  */
249 void
rwlock_error(const rwlock_t * rp,const char * who,const char * msg)250 rwlock_error(const rwlock_t *rp, const char *who, const char *msg)
251 {
252 	rwlock_t rcopy;
253 	uint32_t rwstate;
254 	char buf[800];
255 	uberdata_t *udp;
256 	ulwp_t *self;
257 	lwpid_t lwpid;
258 	pid_t pid;
259 	int process;
260 
261 	/*
262 	 * Take a snapshot of the rwlock before it changes (we hope!).
263 	 * Use memcpy() rather than 'rcopy = *rp' in case rp is unaligned.
264 	 */
265 	(void) memcpy(&rcopy, rp, sizeof (rcopy));
266 
267 	/* avoid recursion deadlock */
268 	if ((self = __curthread()) != NULL) {
269 		if (assert_thread == self)
270 			_exit(127);
271 		enter_critical(self);
272 		(void) _lwp_mutex_lock(&assert_lock);
273 		assert_thread = self;
274 		lwpid = self->ul_lwpid;
275 		udp = self->ul_uberdata;
276 		pid = udp->pid;
277 	} else {
278 		self = NULL;
279 		(void) _lwp_mutex_lock(&assert_lock);
280 		lwpid = _lwp_self();
281 		udp = &__uberdata;
282 		pid = getpid();
283 	}
284 
285 	rwstate = (uint32_t)rcopy.rwlock_readers;
286 	process = (rcopy.rwlock_type & USYNC_PROCESS);
287 
288 	(void) strcpy(buf,
289 	    "\n*** _THREAD_ERROR_DETECTION: lock usage error detected ***\n");
290 	(void) strcat(buf, who);
291 	(void) strcat(buf, "(");
292 	ultos((uint64_t)(uintptr_t)rp, 16, buf + strlen(buf));
293 	(void) strcat(buf, "): ");
294 	(void) strcat(buf, msg);
295 	(void) strcat(buf, "\ncalling thread is ");
296 	ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
297 	(void) strcat(buf, " thread-id ");
298 	ultos((uint64_t)lwpid, 10, buf + strlen(buf));
299 	if (process) {
300 		(void) strcat(buf, " in process ");
301 		ultos((uint64_t)pid, 10, buf + strlen(buf));
302 	}
303 	if (rwstate & URW_WRITE_LOCKED) {
304 		(void) strcat(buf, "\nthe writer lock owner is ");
305 		ultos((uint64_t)rcopy.rwlock_owner, 16,
306 		    buf + strlen(buf));
307 		if (process) {
308 			(void) strcat(buf, " in process ");
309 			ultos((uint64_t)rcopy.rwlock_ownerpid, 10,
310 			    buf + strlen(buf));
311 		}
312 	} else if (rwstate & URW_READERS_MASK) {
313 		(void) strcat(buf, "\nthe reader lock is held by ");
314 		ultos((uint64_t)(rwstate & URW_READERS_MASK), 10,
315 		    buf + strlen(buf));
316 		(void) strcat(buf, " readers");
317 	} else {
318 		(void) strcat(buf, "\nthe lock is unowned");
319 	}
320 	if (rwstate & URW_HAS_WAITERS)
321 		(void) strcat(buf, "\nand the lock appears to have waiters");
322 	(void) strcat(buf, "\n\n");
323 	(void) __write(2, buf, strlen(buf));
324 	if (udp->uberflags.uf_thread_error_detection >= 2)
325 		Abort(buf, sizeof (buf));
326 	assert_thread = NULL;
327 	(void) _lwp_mutex_unlock(&assert_lock);
328 	if (self != NULL)
329 		exit_critical(self);
330 }
331 
332 /*
333  * Report a thread usage error.
334  * Not called if _THREAD_ERROR_DETECTION=0.
335  * Writes message and continues execution if _THREAD_ERROR_DETECTION=1.
336  * Writes message and dumps core if _THREAD_ERROR_DETECTION=2.
337  */
338 void
thread_error(const char * msg)339 thread_error(const char *msg)
340 {
341 	char buf[800];
342 	uberdata_t *udp;
343 	ulwp_t *self;
344 	lwpid_t lwpid;
345 
346 	/* avoid recursion deadlock */
347 	if ((self = __curthread()) != NULL) {
348 		if (assert_thread == self)
349 			_exit(127);
350 		enter_critical(self);
351 		(void) _lwp_mutex_lock(&assert_lock);
352 		assert_thread = self;
353 		lwpid = self->ul_lwpid;
354 		udp = self->ul_uberdata;
355 	} else {
356 		self = NULL;
357 		(void) _lwp_mutex_lock(&assert_lock);
358 		lwpid = _lwp_self();
359 		udp = &__uberdata;
360 	}
361 
362 	(void) strcpy(buf, "\n*** _THREAD_ERROR_DETECTION: "
363 	    "thread usage error detected ***\n*** ");
364 	(void) strcat(buf, msg);
365 
366 	(void) strcat(buf, "\n*** calling thread is ");
367 	ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
368 	(void) strcat(buf, " thread-id ");
369 	ultos((uint64_t)lwpid, 10, buf + strlen(buf));
370 	(void) strcat(buf, "\n\n");
371 	(void) __write(2, buf, strlen(buf));
372 	if (udp->uberflags.uf_thread_error_detection >= 2)
373 		Abort(buf, sizeof (buf));
374 	assert_thread = NULL;
375 	(void) _lwp_mutex_unlock(&assert_lock);
376 	if (self != NULL)
377 		exit_critical(self);
378 }
379 
380 /*
381  * We use __assfail() because the libc __assert() calls
382  * gettext() which calls malloc() which grabs a mutex.
383  * We do everything without calling standard i/o.
384  * assfail() and _assfail() are exported functions;
385  * __assfail() is private to libc.
386  */
387 #pragma weak _assfail = __assfail
388 void
__assfail(const char * assertion,const char * filename,int line_num)389 __assfail(const char *assertion, const char *filename, int line_num)
390 {
391 	char buf[800];	/* no assert() message in the library is this long */
392 	ulwp_t *self;
393 	lwpid_t lwpid;
394 
395 	/* avoid recursion deadlock */
396 	if ((self = __curthread()) != NULL) {
397 		if (assert_thread == self)
398 			_exit(127);
399 		enter_critical(self);
400 		(void) _lwp_mutex_lock(&assert_lock);
401 		assert_thread = self;
402 		lwpid = self->ul_lwpid;
403 	} else {
404 		self = NULL;
405 		(void) _lwp_mutex_lock(&assert_lock);
406 		lwpid = _lwp_self();
407 	}
408 
409 	/*
410 	 * This is a hack, but since the Abort function isn't exported
411 	 * to outside consumers, libzpool's vpanic() function calls
412 	 * assfail() with a filename set to NULL. In that case, it'd be
413 	 * best not to print "assertion failed" since it was a panic and
414 	 * not an assertion failure.
415 	 */
416 	if (filename == NULL) {
417 		(void) strcpy(buf, "failure for thread ");
418 	} else {
419 		(void) strcpy(buf, "assertion failed for thread ");
420 	}
421 
422 	ultos((uint64_t)(uintptr_t)self, 16, buf + strlen(buf));
423 	(void) strcat(buf, ", thread-id ");
424 	ultos((uint64_t)lwpid, 10, buf + strlen(buf));
425 	(void) strcat(buf, ": ");
426 	(void) strcat(buf, assertion);
427 
428 	if (filename != NULL) {
429 		(void) strcat(buf, ", file ");
430 		(void) strcat(buf, filename);
431 		(void) strcat(buf, ", line ");
432 		ultos((uint64_t)line_num, 10, buf + strlen(buf));
433 	}
434 
435 	(void) strcat(buf, "\n");
436 	(void) __write(2, buf, strlen(buf));
437 	/*
438 	 * We could replace the call to Abort() with the following code
439 	 * if we want just to issue a warning message and not die.
440 	 *	assert_thread = NULL;
441 	 *	_lwp_mutex_unlock(&assert_lock);
442 	 *	if (self != NULL)
443 	 *		exit_critical(self);
444 	 */
445 	Abort(buf, sizeof (buf));
446 }
447 
448 /*
449  * We define and export this version of assfail() just because libaio
450  * used to define and export it, needlessly.  Now that libaio is folded
451  * into libc, we need to continue this for ABI/version reasons.
452  * We don't use "#pragma weak assfail __assfail" in order to avoid
453  * warnings from the check_fnames utility at build time for libraries
454  * that define their own version of assfail().
455  *
456  * Additionally, ASSERT() and VERIFY() in <sys/debug.h> can invoke assfail().
457  *
458  * We would like this to be declared _NORETURN, but some caution is in order
459  * as alternate implementations of assfail() exist -- notably the one in
460  * libfakekernel -- which can return under some circumstances.
461  */
462 void
assfail(const char * assertion,const char * filename,int line_num)463 assfail(const char *assertion, const char *filename, int line_num)
464 {
465 	__assfail(assertion, filename, line_num);
466 	/* NOTREACHED */
467 }
468 
469 void
assfail3(const char * assertion,uintmax_t lv,const char * op,uintmax_t rv,const char * filename,int line_num)470 assfail3(const char *assertion, uintmax_t lv, const char *op, uintmax_t rv,
471     const char *filename, int line_num)
472 {
473 	char buf[1000];
474 	(void) strcpy(buf, assertion);
475 	(void) strcat(buf, " (");
476 	ultos((uint64_t)lv, 16, buf + strlen(buf));
477 	(void) strcat(buf, " ");
478 	(void) strcat(buf, op);
479 	(void) strcat(buf, " ");
480 	ultos((uint64_t)rv, 16, buf + strlen(buf));
481 	(void) strcat(buf, ")");
482 	__assfail(buf, filename, line_num);
483 }
484