summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoranton <anton@openbsd.org>2019-03-18 17:30:07 +0000
committeranton <anton@openbsd.org>2019-03-18 17:30:07 +0000
commit286735181e96e624bfb5a61f2ba75479ca1259d2 (patch)
treed400fbb6e897f396b4bbde7ab3ef99e665811bb5
parentAdd a couple of checks to ensure option data fits into the proposal (diff)
downloadwireguard-openbsd-286735181e96e624bfb5a61f2ba75479ca1259d2.tar.xz
wireguard-openbsd-286735181e96e624bfb5a61f2ba75479ca1259d2.zip
Add kubsan(4), a undefined behavior sanitizer for the kernel. It's
capable of detecting undefined behavior at runtime and all findings are printed to the system console, including the offending line in the source code. kubsan is limited to architectures using Clang as their default compiler and is not enabled by default. Derived from the NetBSD implementation. ok kettenis@ visa@
-rw-r--r--share/man/man4/Makefile6
-rw-r--r--share/man/man4/kubsan.498
-rw-r--r--share/man/man4/options.48
-rw-r--r--sys/arch/amd64/conf/Makefile.amd647
-rw-r--r--sys/conf/files3
-rw-r--r--sys/kern/subr_kubsan.c491
6 files changed, 606 insertions, 7 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index cff5be21dbb..f693b6b804c 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.702 2019/03/11 10:48:45 dlg Exp $
+# $OpenBSD: Makefile,v 1.703 2019/03/18 17:30:07 anton Exp $
MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
acpi.4 acpiac.4 acpials.4 acpiasus.4 acpibat.4 \
@@ -38,8 +38,8 @@ MAN= aac.4 abcrtc.4 ac97.4 acphy.4 acrtc.4 \
ip.4 ip6.4 ipcomp.4 ipgphy.4 ipmi.4 ips.4 ipsec.4 ipw.4 \
isa.4 isagpio.4 isapnp.4 islrtc.4 it.4 itherm.4 iwi.4 iwn.4 iwm.4 \
ix.4 ixgb.4 ixl.4 jmb.4 jme.4 jmphy.4 \
- kate.4 kcov.4 km.4 ksyms.4 kue.4 lc.4 lge.4 lii.4 lisa.4 lm.4 \
- lmenv.4 lmn.4 lmtemp.4 lo.4 lpt.4 lxtphy.4 luphy.4 \
+ kate.4 kcov.4 km.4 ksyms.4 kubsan.4 kue.4 lc.4 lge.4 lii.4 lisa.4 \
+ lm.4 lmenv.4 lmn.4 lmtemp.4 lo.4 lpt.4 lxtphy.4 luphy.4 \
maestro.4 mainbus.4 malo.4 maxds.4 maxrtc.4 maxtmp.4 mbg.4 midi.4 \
mii.4 mfi.4 \
mfii.4 mlphy.4 mobileip.4 moscom.4 mos.4 mpe.4 mpath.4 mpi.4 mpii.4 \
diff --git a/share/man/man4/kubsan.4 b/share/man/man4/kubsan.4
new file mode 100644
index 00000000000..23c91d17a13
--- /dev/null
+++ b/share/man/man4/kubsan.4
@@ -0,0 +1,98 @@
+.\" $OpenBSD: kubsan.4,v 1.1 2019/03/18 17:30:07 anton Exp $
+.\"
+.\" Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.Dd $Mdocdate: March 18 2019 $
+.Dt KUBSAN 4
+.Os
+.Sh NAME
+.Nm kubsan
+.Nd kernel undefined behavior sanitizer
+.Sh SYNOPSIS
+.Cd option KUBSAN
+.Sh DESCRIPTION
+.Nm
+detects undefined behavior at runtime inside the kernel.
+Detected undefined behavior is printed to the system console,
+including the offending line in the source code.
+.Pp
+By default,
+.Nm
+is not enabled but instead requires the following line to be present in the
+kernel configuration:
+.Bd -literal -offset indent
+option KUBSAN
+.Ed
+.Pp
+The following undefined behavior is detected:
+.Bl -tag -width 4n
+.It Integer overflow
+The result of an aritmetic computation on two integer operands cannot be
+represented by the destination type.
+.It Negate overflow
+Negation of an integer cannot be represented by the destination type.
+.It Pointer overflow
+Pointer aritmetic overflow.
+.It Out of bounds
+Array indexing out of bounds, limited to cases where the size of the array
+can be statically determined.
+.It Shift out of bounds
+Undefined logical shift caused by:
+.Bl -dash
+.It
+The shift amount being negative.
+.It
+The shift operand being negative.
+.It
+The shift amount exceeds the number of bits as given by the shift operand
+type.
+.It
+The result of the shift computation cannot be represented by the destination
+type.
+.El
+.It Invalid load
+Loading a value that cannot be represented by the destination type.
+.It Type mismatch
+Mismatch between pointer and value type caused by:
+.Bl -dash
+.It
+A pointer which does not fulfill the alignment requirements of the value type.
+.It
+A pointer to an address which lacks sufficient space to store the value type.
+.El
+.It Unreachable
+Execution reached passed a function annotated as
+.Dv __dead .
+.El
+.Sh SEE ALSO
+.Xr options 4
+.Sh HISTORY
+The
+.Nm
+implementation
+is derived from
+.Nx
+and first appeared in
+.Ox 6.5 .
+.Sh AUTHORS
+The
+.Nm
+implementation was written by
+.An Anton Lindqvist Aq Mt anton@openbsd.org .
+.Sh CAVEATS
+The
+.Nm
+implementation is limited to architectures using
+.Xr clang 1
+as their default compiler.
diff --git a/share/man/man4/options.4 b/share/man/man4/options.4
index 6c9f58b9935..dc042fe92f7 100644
--- a/share/man/man4/options.4
+++ b/share/man/man4/options.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: options.4,v 1.262 2019/02/07 15:11:38 visa Exp $
+.\" $OpenBSD: options.4,v 1.263 2019/03/18 17:30:07 anton Exp $
.\" $NetBSD: options.4,v 1.21 1997/06/25 03:13:00 thorpej Exp $
.\"
.\" Copyright (c) 1998 Theo de Raadt
@@ -34,7 +34,7 @@
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\"
-.Dd $Mdocdate: February 7 2019 $
+.Dd $Mdocdate: March 18 2019 $
.Dt OPTIONS 4
.Os
.Sh NAME
@@ -114,6 +114,10 @@ Adds hooks for the system call tracing facility, which allows users to
watch the system call invocation behavior of processes.
See
.Xr ktrace 1 .
+.It Cd option KUBSAN
+Detect undefined behavior in the kernel.
+See
+.Xr kubsan 4 .
.It Cd option NO_PROPOLICE
Do not compile the kernel with the ProPolice stack protection.
See
diff --git a/sys/arch/amd64/conf/Makefile.amd64 b/sys/arch/amd64/conf/Makefile.amd64
index 6ed2573a362..93c45a0a1a9 100644
--- a/sys/arch/amd64/conf/Makefile.amd64
+++ b/sys/arch/amd64/conf/Makefile.amd64
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.amd64,v 1.110 2019/02/03 10:58:51 dlg Exp $
+# $OpenBSD: Makefile.amd64,v 1.111 2019/03/18 17:30:08 anton Exp $
# For instructions on building kernels consult the config(8) and options(4)
# manual pages.
@@ -98,6 +98,11 @@ LINKFLAGS+= -S
PROF= -fsanitize-coverage=trace-pc,trace-cmp
.endif
+.if ${IDENT:M-DKUBSAN} && ${COMPILER_VERSION:Mclang}
+CFLAGS+= -fsanitize=undefined
+CFLAGS+= -fno-wrapv
+.endif
+
%LOAD
# cc's -MD puts the source and output paths in the dependency file;
diff --git a/sys/conf/files b/sys/conf/files
index ac86f06519c..357c7d0f551 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.669 2019/02/28 06:06:28 dlg Exp $
+# $OpenBSD: files,v 1.670 2019/03/18 17:30:08 anton Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -711,6 +711,7 @@ file kern/subr_disk.c
file kern/subr_evcount.c
file kern/subr_extent.c
file kern/subr_hibernate.c hibernate
+file kern/subr_kubsan.c kubsan
file kern/subr_log.c
file kern/subr_percpu.c
file kern/subr_poison.c diagnostic
diff --git a/sys/kern/subr_kubsan.c b/sys/kern/subr_kubsan.c
new file mode 100644
index 00000000000..f6713903460
--- /dev/null
+++ b/sys/kern/subr_kubsan.c
@@ -0,0 +1,491 @@
+/* $OpenBSD: subr_kubsan.c,v 1.1 2019/03/18 17:30:08 anton Exp $ */
+
+/*
+ * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/atomic.h>
+#include <sys/syslimits.h>
+#include <sys/systm.h>
+
+#define NUMBER_BUFSIZ 32
+#define LOCATION_BUFSIZ (PATH_MAX + 32) /* filename:line:column */
+#define LOCATION_REPORTED (1U << 30)
+
+#define NBITS(typ) (1 << ((typ)->t_info >> 1))
+#define SIGNED(typ) ((typ)->t_info & 1)
+
+struct type_descriptor {
+ uint16_t t_kind;
+ uint16_t t_info;
+ char t_name[1]; /* type name as variable length array */
+};
+
+struct source_location {
+ const char *sl_filename;
+ uint32_t sl_line;
+ uint32_t sl_column;
+};
+
+struct invalid_value_data {
+ struct source_location d_src;
+ struct type_descriptor *d_type;
+};
+
+struct out_of_bounds_data {
+ struct source_location d_src;
+ struct type_descriptor *d_atype; /* array type */
+ struct type_descriptor *d_itype; /* index type */
+};
+
+struct overflow_data {
+ struct source_location d_src;
+ struct type_descriptor *d_type;
+};
+
+struct pointer_overflow_data {
+ struct source_location d_src;
+};
+
+struct shift_out_of_bounds_data {
+ struct source_location d_src;
+ struct type_descriptor *d_ltype;
+ struct type_descriptor *d_rtype;
+};
+
+struct unreachable_data {
+ struct source_location d_src;
+};
+
+struct type_mismatch {
+ struct source_location d_src;
+ struct type_descriptor *d_type;
+ uint8_t d_align; /* log2 alignment */
+ uint8_t d_kind;
+};
+
+void kubsan_handle_load_invalid_value(struct invalid_value_data *,
+ unsigned long);
+void kubsan_handle_negate_overflow(struct overflow_data *, unsigned long);
+void kubsan_handle_out_of_bounds(struct out_of_bounds_data *, unsigned long);
+void kubsan_handle_overflow(struct overflow_data *, unsigned long,
+ unsigned long, char);
+void kubsan_handle_pointer_overflow(struct pointer_overflow_data *,
+ unsigned long, unsigned long);
+void kubsan_handle_type_mismatch(struct type_mismatch *, unsigned long);
+void kubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *,
+ unsigned long, unsigned long);
+void kubsan_handle_ureachable(struct unreachable_data *);
+
+int64_t kubsan_deserialize_int(struct type_descriptor *,
+ unsigned long);
+uint64_t kubsan_deserialize_uint(struct type_descriptor *,
+ unsigned long);
+void kubsan_format_int(struct type_descriptor *, unsigned long,
+ char *, size_t);
+void kubsan_format_location(struct source_location *, char *,
+ size_t);
+int kubsan_is_reported(struct source_location *);
+const char *kubsan_kind(uint8_t);
+void kubsan_report(const char *, ...)
+ __attribute__((__format__(__kprintf__, 1, 2)));
+
+static int is_negative(struct type_descriptor *, unsigned long);
+static int is_shift_exponent_too_large(struct type_descriptor *,
+ unsigned long);
+
+#ifdef KUBSAN_WATCH
+int kubsan_watch = 2;
+#else
+int kubsan_watch = 1;
+#endif
+
+/*
+ * Compiling the kernel with `-fsanitize=undefined' will cause the following
+ * functions to be called when a sanitizer detects undefined behavior.
+ * Some sanitizers are omitted since they are only applicable to C++.
+ *
+ * Every __ubsan_*() sanitizer function also has a corresponding
+ * __ubsan_*_abort() function as part of the ABI provided by Clang.
+ * But, since the kernel never is compiled with `fno-sanitize-recover' for
+ * obvious reasons, they are also omitted.
+ */
+
+void
+__ubsan_handle_add_overflow(struct overflow_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ kubsan_handle_overflow(data, lhs, rhs, '+');
+}
+
+void
+__ubsan_handle_builtin_unreachable(struct unreachable_data *data)
+{
+ kubsan_handle_ureachable(data);
+}
+
+void
+__ubsan_handle_divrem_overflow(struct overflow_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ kubsan_handle_overflow(data, lhs, rhs, '/');
+}
+
+void
+__ubsan_handle_load_invalid_value(struct invalid_value_data *data,
+ unsigned long val)
+{
+ kubsan_handle_load_invalid_value(data, val);
+}
+
+void
+__ubsan_handle_mul_overflow(struct overflow_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ kubsan_handle_overflow(data, lhs, rhs, '*');
+}
+
+void
+__ubsan_handle_negate_overflow(struct overflow_data *data, unsigned long val)
+{
+ kubsan_handle_negate_overflow(data, val);
+}
+
+void
+__ubsan_handle_out_of_bounds(struct out_of_bounds_data *data,
+ unsigned long idx)
+{
+ kubsan_handle_out_of_bounds(data, idx);
+}
+
+void
+__ubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
+ unsigned long base, unsigned long res)
+{
+ kubsan_handle_pointer_overflow(data, base, res);
+}
+
+void
+__ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ kubsan_handle_shift_out_of_bounds(data, lhs, rhs);
+}
+
+void
+__ubsan_handle_sub_overflow(struct overflow_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ kubsan_handle_overflow(data, lhs, rhs, '-');
+}
+
+void
+__ubsan_handle_type_mismatch_v1(struct type_mismatch *data,
+ unsigned long ptr)
+{
+ kubsan_handle_type_mismatch(data, ptr);
+}
+
+void
+kubsan_handle_load_invalid_value(struct invalid_value_data *data,
+ unsigned long val)
+{
+ char bloc[LOCATION_BUFSIZ];
+ char bval[NUMBER_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_format_int(data->d_type, val, bval, sizeof(bval));
+ kubsan_report("kubsan: %s: load invalid value: load of value %s is "
+ "not a valid value for type %s\n",
+ bloc, bval, data->d_type->t_name);
+}
+
+void
+kubsan_handle_negate_overflow(struct overflow_data *data, unsigned long val)
+{
+ char bloc[LOCATION_BUFSIZ];
+ char bval[NUMBER_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_format_int(data->d_type, val, bval, sizeof(bval));
+ kubsan_report("kubsan: %s: negate overflow: negation of %s cannot be "
+ "represented in type %s\n",
+ bloc, bval, data->d_type->t_name);
+}
+
+void
+kubsan_handle_out_of_bounds(struct out_of_bounds_data *data,
+ unsigned long idx)
+{
+ char bloc[LOCATION_BUFSIZ];
+ char bidx[NUMBER_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_format_int(data->d_itype, idx, bidx, sizeof(bidx));
+ kubsan_report("kubsan: %s: out of bounds: index %s is out of range "
+ "for type %s\n",
+ bloc, bidx, data->d_atype->t_name);
+}
+
+void
+kubsan_handle_overflow(struct overflow_data *data, unsigned long rhs,
+ unsigned long lhs, char op)
+{
+ char bloc[LOCATION_BUFSIZ];
+ char blhs[NUMBER_BUFSIZ];
+ char brhs[NUMBER_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_format_int(data->d_type, lhs, blhs, sizeof(blhs));
+ kubsan_format_int(data->d_type, rhs, brhs, sizeof(brhs));
+ kubsan_report("kubsan: %s: %s integer overflow: %s %c %s cannot "
+ "be represented in type %s\n",
+ bloc, SIGNED(data->d_type) ? "signed" : "unsigned",
+ blhs, op, brhs, data->d_type->t_name);
+}
+
+void
+kubsan_handle_pointer_overflow(struct pointer_overflow_data *data,
+ unsigned long base, unsigned long res)
+{
+ char bloc[LOCATION_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_report("kubsan: %s: pointer overflow: pointer expression with"
+ " base %#lx overflowed to %#lx\n",
+ bloc, base, res);
+}
+
+void
+kubsan_handle_type_mismatch(struct type_mismatch *data, unsigned long ptr)
+{
+ char bloc[LOCATION_BUFSIZ];
+ unsigned long align = 1UL << data->d_align;
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ if (ptr == 0UL)
+ kubsan_report("kubsan: %s: type mismatch: %s null pointer of "
+ "type %s\n",
+ bloc, kubsan_kind(data->d_kind), data->d_type->t_name);
+ else if (ptr & (align - 1))
+ kubsan_report("kubsan: %s: type mismatch: %s misaligned address "
+ "%p for type %s which requires %lu byte alignment\n",
+ bloc, kubsan_kind(data->d_kind), (void *)ptr,
+ data->d_type->t_name, align);
+ else
+ kubsan_report("kubsan: %s: type mismatch: %s address %p with "
+ "insufficient space for an object of type %s\n",
+ bloc, kubsan_kind(data->d_kind), (void *)ptr,
+ data->d_type->t_name);
+}
+
+void
+kubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
+ unsigned long lhs, unsigned long rhs)
+{
+ char bloc[LOCATION_BUFSIZ];
+ char blhs[NUMBER_BUFSIZ];
+ char brhs[NUMBER_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_format_int(data->d_ltype, lhs, blhs, sizeof(blhs));
+ kubsan_format_int(data->d_rtype, rhs, brhs, sizeof(brhs));
+ if (is_negative(data->d_rtype, rhs))
+ kubsan_report("kubsan: %s: shift: shift exponent %s is "
+ "negative\n",
+ bloc, brhs);
+ else if (is_shift_exponent_too_large(data->d_rtype, rhs))
+ kubsan_report("kubsan: %s: shift: shift exponent %s is too "
+ "large for %u-bit type\n",
+ bloc, brhs, NBITS(data->d_rtype));
+ else if (is_negative(data->d_ltype, lhs))
+ kubsan_report("kubsan: %s: shift: left shift of negative "
+ "value %s\n",
+ bloc, blhs);
+ else
+ kubsan_report("kubsan: %s: shift: left shift of %s by %s "
+ "places cannot be represented in type %s\n",
+ bloc, blhs, brhs, data->d_ltype->t_name);
+}
+
+void
+kubsan_handle_ureachable(struct unreachable_data *data)
+{
+ char bloc[LOCATION_BUFSIZ];
+
+ if (kubsan_is_reported(&data->d_src))
+ return;
+
+ kubsan_format_location(&data->d_src, bloc, sizeof(bloc));
+ kubsan_report("kubsan: %s: unreachable: calling "
+ "__builtin_unreachable()\n",
+ bloc);
+}
+
+int64_t
+kubsan_deserialize_int(struct type_descriptor *typ, unsigned long val)
+{
+ switch (NBITS(typ)) {
+ case 8:
+ return ((int8_t)val);
+ case 16:
+ return ((int16_t)val);
+ case 32:
+ return ((int32_t)val);
+ case 64:
+ default:
+ return ((int64_t)val);
+ }
+}
+
+uint64_t
+kubsan_deserialize_uint(struct type_descriptor *typ, unsigned long val)
+{
+ switch (NBITS(typ)) {
+ case 8:
+ return ((uint8_t)val);
+ case 16:
+ return ((uint16_t)val);
+ case 32:
+ return ((uint32_t)val);
+ case 64:
+ default:
+ return ((uint64_t)val);
+ }
+}
+
+void
+kubsan_format_int(struct type_descriptor *typ, unsigned long val,
+ char *buf, size_t bufsiz)
+{
+ switch (typ->t_kind) {
+ case 0: /* integer */
+ if (SIGNED(typ)) {
+ int64_t i = kubsan_deserialize_int(typ, val);
+ snprintf(buf, bufsiz, "%lld", i);
+ } else {
+ uint64_t u = kubsan_deserialize_uint(typ, val);
+ snprintf(buf, bufsiz, "%llu", u);
+ }
+ break;
+ default:
+ snprintf(buf, bufsiz, "%#x<NaN>", typ->t_kind);
+ }
+}
+
+void
+kubsan_format_location(struct source_location *src, char *buf,
+ size_t bufsiz)
+{
+ snprintf(buf, bufsiz, "%s:%u:%u",
+ src->sl_filename, src->sl_line & ~LOCATION_REPORTED,
+ src->sl_column);
+}
+
+int
+kubsan_is_reported(struct source_location *src)
+{
+ uint32_t *line = &src->sl_line;
+ uint32_t prev;
+
+ /*
+ * Treat everything as reported when disabled.
+ * Otherwise, new violations would go by unnoticed.
+ */
+ if (__predict_false(kubsan_watch == 0))
+ return (1);
+
+ do {
+ prev = *line;
+ /* If already reported, avoid redundant atomic operation. */
+ if (prev & LOCATION_REPORTED)
+ break;
+ } while (atomic_cas_uint(line, prev, prev | LOCATION_REPORTED) != prev);
+
+ return (prev & LOCATION_REPORTED);
+}
+
+const char *
+kubsan_kind(uint8_t kind)
+{
+ static const char *kinds[] = {
+ "load of",
+ "store to",
+ "reference binding to",
+ "member access within",
+ "member call on",
+ "constructor call on",
+ "downcast of",
+ "downcast of",
+ "upcast of",
+ "cast to virtual base of",
+ "_Nonnull binding to",
+ "dynamic operation on"
+ };
+
+ if (kind >= nitems(kinds))
+ return ("?");
+
+ return (kinds[kind]);
+}
+
+void
+kubsan_report(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+
+#ifdef DDB
+ if (kubsan_watch == 2)
+ db_enter();
+#endif
+}
+
+static int
+is_negative(struct type_descriptor *typ, unsigned long val)
+{
+ return (SIGNED(typ) && kubsan_deserialize_int(typ, val) < 0);
+}
+
+static int
+is_shift_exponent_too_large(struct type_descriptor *typ, unsigned long val)
+{
+ return (kubsan_deserialize_int(typ, val) >= NBITS(typ));
+}