Page MenuHomeFreeBSD

D46193.diff
No OneTemporary

D46193.diff

diff --git a/stand/common/load_elf.c b/stand/common/load_elf.c
--- a/stand/common/load_elf.c
+++ b/stand/common/load_elf.c
@@ -539,6 +539,7 @@
Elf_Dyn *dp;
Elf_Addr adp;
Elf_Addr ctors;
+ Elf_Addr kcfi;
int ndp;
int symstrindex;
int symtabindex;
@@ -725,16 +726,26 @@
shdr[ehdr->e_shstrndx].sh_offset, chunk);
if (shstr) {
for (i = 0; i < ehdr->e_shnum; i++) {
- if (strcmp(shstr + shdr[i].sh_name,
- ".ctors") != 0)
- continue;
- ctors = shdr[i].sh_addr;
- file_addmetadata(fp, MODINFOMD_CTORS_ADDR,
- sizeof(ctors), &ctors);
- size = shdr[i].sh_size;
- file_addmetadata(fp, MODINFOMD_CTORS_SIZE,
- sizeof(size), &size);
- break;
+ if (strcmp(shstr + shdr[i].sh_name, ".ctors") ==
+ 0) {
+ ctors = shdr[i].sh_addr;
+ file_addmetadata(fp,
+ MODINFOMD_CTORS_ADDR, sizeof(ctors),
+ &ctors);
+ size = shdr[i].sh_size;
+ file_addmetadata(fp,
+ MODINFOMD_CTORS_SIZE, sizeof(size),
+ &size);
+ }
+ else if (strcmp(shstr + shdr[i].sh_name,
+ ".kcfi_traps") == 0) {
+ kcfi = shdr[i].sh_addr;
+ file_addmetadata(fp, MODINFOMD_CFI_ADDR,
+ sizeof(kcfi), &kcfi);
+ size = shdr[i].sh_size;
+ file_addmetadata(fp, MODINFOMD_CFI_SIZE,
+ sizeof(size), &size);
+ }
}
free(shstr);
}
diff --git a/sys/amd64/amd64/cfi.c b/sys/amd64/amd64/cfi.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/amd64/cfi.c
@@ -0,0 +1,114 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 202 ShengYi Hung
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/systm.h>
+
+#include <machine/cpu.h>
+#include <machine/frame.h>
+#include <machine/trap.h>
+
+static int
+regoff(int reg)
+{
+#define _MATCH_REG(i, reg) \
+ case i: \
+ return ( \
+ offsetof(struct trapframe, tf_##reg) / sizeof(register_t))
+ switch (reg) {
+ _MATCH_REG(0, rax);
+ _MATCH_REG(1, rcx);
+ _MATCH_REG(2, rdx);
+ _MATCH_REG(3, rbx);
+ _MATCH_REG(4, rsp); /* SIB when mod != 3 */
+ _MATCH_REG(5, rbp);
+ _MATCH_REG(6, rsi);
+ _MATCH_REG(7, rdi);
+ _MATCH_REG(8, r8); /* REX.R is set */
+ _MATCH_REG(9, r9);
+ _MATCH_REG(10, r10);
+ _MATCH_REG(11, r11);
+ _MATCH_REG(12, r12);
+ _MATCH_REG(13, r13);
+ _MATCH_REG(14, r14);
+ _MATCH_REG(15, r15);
+ }
+#undef _MATCH_REG
+ return (0);
+}
+
+bool
+decode_cfi_frame(struct trapframe *tf, uintptr_t *callee_addr, uint32_t *type)
+{
+ int idx;
+ unsigned char buffer[14];
+
+ for (idx = 0; idx < 14; idx++)
+ if (safe_read(tf->tf_rip - 12 + idx, buffer + idx) != 0)
+ return (false);
+
+ /*
+ * Clang currently emits the following instruction sequence for CFI
+ * checks:
+ *
+ * mov $typeid, %reg
+ * add $callee-16, %reg
+ * je .Lcorrect
+ * ud2
+ * .Lcorrect:
+ *
+ * Detect a potential CFI fault by checking whether the instructions
+ * preceding the trap match the expected `mov` (0xba), `add` (0x03),
+ * and `ud2` sequence. If they do, the trap was likely generated by a
+ * failed CFI check.
+ *
+ * Changing this sequence would break compatibility with existing CFI
+ * implementations, so it is unlikely to change significantly. This
+ * makes the pattern match suitable as a fast-path check.
+ */
+ if (buffer[1] != 0xba)
+ return (false);
+ if (buffer[7] != 0x03)
+ return (false);
+ if (buffer[12] != 0x0f || buffer[13] != 0x0b)
+ return (false);
+
+ *type = *((uint32_t *)(buffer + 2));
+
+ /*
+ * Decode register(prefix & REX.R) | (MODRM.rm(3bit))
+ */
+ *callee_addr = ((register_t *)tf)[regoff(
+ (((buffer[6] >> 2) & 0x1) << 3) | (buffer[8] & 0x7))];
+ return (true);
+}
+
+void
+post_cfi(struct trapframe *frame)
+{
+ TRAPF_PC(frame) += 2;
+}
diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c
--- a/sys/amd64/amd64/trap.c
+++ b/sys/amd64/amd64/trap.c
@@ -531,6 +531,14 @@
(void)trap_pfault(frame, false, NULL, NULL);
return;
+ case T_PRIVINFLT:
+#ifdef KCFI
+ /* Triggered by ud2 with kCFI enabled */
+ if (cfi_handler(frame))
+ return;
+#endif
+ break;
+
case T_DNA:
if (PCB_USER_FPU(td->td_pcb))
panic("Unregistered use of FPU in kernel");
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3952,6 +3952,7 @@
kern/subr_bus_dma.c standard
kern/subr_bufring.c standard
kern/subr_capability.c standard
+kern/subr_cfi.c optional kcfi
kern/subr_clock.c standard
kern/subr_compressor.c standard \
compile-with "${NORMAL_C} -I$S/contrib/zstd/lib/freebsd"
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -64,6 +64,7 @@
amd64/amd64/bios.c standard
amd64/amd64/bpf_jit_machdep.c optional bpf_jitter
amd64/amd64/copyout.c standard
+amd64/amd64/cfi.c optional kcfi
amd64/amd64/cpu_switch.S standard
amd64/amd64/db_disasm.c optional ddb
amd64/amd64/db_interface.c optional ddb
diff --git a/sys/conf/kern.mk b/sys/conf/kern.mk
--- a/sys/conf/kern.mk
+++ b/sys/conf/kern.mk
@@ -290,6 +290,10 @@
-fsanitize=thread
.endif
+.if !empty(KCFI_ENABLED)
+SAN_CFLAGS+= -fsanitize=kcfi
+.endif
+
#
# Kernel Memory SANitizer support
#
diff --git a/sys/conf/kern.post.mk b/sys/conf/kern.post.mk
--- a/sys/conf/kern.post.mk
+++ b/sys/conf/kern.post.mk
@@ -37,6 +37,10 @@
MKMODULESENV+= KCSAN_ENABLED="yes"
.endif
+.if !empty(KCFI_ENABLED)
+MKMODULESENV+= KCFI_ENABLED="yes"
+.endif
+
.if defined(GCOV_CFLAGS)
MKMODULESENV+= GCOV_CFLAGS="${GCOV_CFLAGS}"
.endif
@@ -216,11 +220,15 @@
OBJS_DEPEND_GUESS+= offset.inc assym.inc vnode_if.h ${BEFORE_DEPEND:M*.h} \
${MFILES:T:S/.m$/.h/}
+.if !empty(KCFI_ENABLED)
+MAKEOBJOPT= "-s"
+.endif
+
.for mfile in ${MFILES}
# XXX the low quality .m.o rules gnerated by config are normally used
# instead of the .m.c rules here.
-${mfile:T:S/.m$/.c/}: ${mfile}
- ${AWK} -f $S/tools/makeobjops.awk ${mfile} -c
+${mfile:T:S/.m$/.c/}: ${mfile} $S/tools/makeobjops.awk
+ ${AWK} -f $S/tools/makeobjops.awk ${mfile} -c ${MAKEOBJOPT}
${mfile:T:S/.m$/.h/}: ${mfile}
${AWK} -f $S/tools/makeobjops.awk ${mfile} -h
.endfor
diff --git a/sys/conf/kern.pre.mk b/sys/conf/kern.pre.mk
--- a/sys/conf/kern.pre.mk
+++ b/sys/conf/kern.pre.mk
@@ -95,6 +95,7 @@
COMPAT_FREEBSD32_ENABLED!= grep COMPAT_FREEBSD32 opt_global.h || true ; echo
+KCFI_ENABLED!= grep KCFI opt_global.h || true ; echo
KASAN_ENABLED!= grep KASAN opt_global.h || true ; echo
KCSAN_ENABLED!= grep KCSAN opt_global.h || true ; echo
KMSAN_ENABLED!= grep KMSAN opt_global.h || true ; echo
@@ -108,6 +109,10 @@
.endif
.endif
+.if !empty(KCFI_ENABLED)
+MAKEOBJOPT= "-s"
+.endif
+
CFLAGS+= ${GCOV_CFLAGS}
# Put configuration-specific C flags last so that they can override
@@ -144,7 +149,7 @@
NORMAL_S= ${CC:N${CCACHE_BIN}} -c ${ASM_CFLAGS} ${WERROR} ${.IMPSRC}
NORMAL_C_NOWERROR= ${CC} -c ${CFLAGS} ${.IMPSRC}
-NORMAL_M= ${AWK} -f $S/tools/makeobjops.awk ${.IMPSRC} -c ; \
+NORMAL_M= ${AWK} -f $S/tools/makeobjops.awk ${.IMPSRC} -c ${MAKEOBJOPT} ; \
${CC} -c ${CFLAGS} ${WERROR} ${.PREFIX}.c
NORMAL_FW= uudecode -o ${.TARGET} ${.ALLSRC}
diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk
--- a/sys/conf/kmod.mk
+++ b/sys/conf/kmod.mk
@@ -426,6 +426,7 @@
.endfor
.endif
+KCFI_ENABLED= ${KERN_OPTS:MKCFI}
KASAN_ENABLED= ${KERN_OPTS:MKASAN}
KCSAN_ENABLED= ${KERN_OPTS:MKCSAN}
KMSAN_ENABLED= ${KERN_OPTS:MKMSAN}
@@ -455,6 +456,10 @@
${AWK} -f ${SYSDIR}/tools/vnode_if.awk ${SYSDIR}/kern/vnode_if.src -q
.endif
+.if !empty(KCFI_ENABLED)
+MAKEOBJOPT= "-s"
+.endif
+
# Build _if.[ch] from _if.m, and clean them when we're done.
# __MPATH defined in config.mk
_MFILES=${__MPATH:T:O}
@@ -468,7 +473,7 @@
.endif
.endfor # _i
.m.c: ${SYSDIR}/tools/makeobjops.awk
- ${AWK} -f ${SYSDIR}/tools/makeobjops.awk ${.IMPSRC} -c
+ ${AWK} -f ${SYSDIR}/tools/makeobjops.awk ${.IMPSRC} -c ${MAKEOBJOPT}
.m.h: ${SYSDIR}/tools/makeobjops.awk
${AWK} -f ${SYSDIR}/tools/makeobjops.awk ${.IMPSRC} -h
diff --git a/sys/conf/options b/sys/conf/options
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -245,6 +245,7 @@
COVERAGE opt_global.h
KASAN opt_global.h
KCOV
+KCFI opt_global.h
KCSAN opt_global.h
KMSAN opt_global.h
KUBSAN opt_global.h
diff --git a/sys/kern/kern_linker.c b/sys/kern/kern_linker.c
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -674,6 +674,8 @@
lf->exidx_addr = 0;
lf->exidx_size = 0;
#endif
+ lf->kcfi_traps_addr = 0;
+ lf->kcfi_traps_size = 0;
STAILQ_INIT(&lf->common);
TAILQ_INIT(&lf->modules);
TAILQ_INSERT_TAIL(&linker_files, lf, link);
diff --git a/sys/kern/link_elf.c b/sys/kern/link_elf.c
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -358,7 +358,7 @@
}
static void
-link_elf_invoke_cbs(caddr_t addr, size_t size)
+link_elf_invoke_cbs(caddr_t addr, size_t size) __nosanitizekcfi
{
void (**ctor)(void);
size_t i, cnt;
@@ -448,8 +448,8 @@
link_elf_init(void* arg)
{
Elf_Dyn *dp;
- Elf_Addr *ctors_addrp;
- Elf_Size *ctors_sizep;
+ Elf_Addr *addrp;
+ Elf_Size *sizep;
caddr_t baseptr, sizeptr;
elf_file_t ef;
const char *modname;
@@ -497,14 +497,22 @@
sizeptr = preload_search_info(preload_kmdp, MODINFO_SIZE);
if (sizeptr != NULL)
linker_kernel_file->size = *(size_t *)sizeptr;
- ctors_addrp = (Elf_Addr *)preload_search_info(preload_kmdp,
- MODINFO_METADATA | MODINFOMD_CTORS_ADDR);
- ctors_sizep = (Elf_Size *)preload_search_info(preload_kmdp,
- MODINFO_METADATA | MODINFOMD_CTORS_SIZE);
- if (ctors_addrp != NULL && ctors_sizep != NULL) {
- linker_kernel_file->ctors_addr = ef->address +
- *ctors_addrp;
- linker_kernel_file->ctors_size = *ctors_sizep;
+ addrp = (Elf_Addr *)preload_search_info(preload_kmdp,
+ MODINFO_METADATA | MODINFOMD_CTORS_ADDR);
+ sizep = (Elf_Size *)preload_search_info(preload_kmdp,
+ MODINFO_METADATA | MODINFOMD_CTORS_SIZE);
+ if (addrp != NULL && sizep != NULL) {
+ linker_kernel_file->ctors_addr = ef->address + *addrp;
+ linker_kernel_file->ctors_size = *sizep;
+ }
+ addrp = (Elf_Addr *)preload_search_info(preload_kmdp,
+ MODINFO_METADATA | MODINFOMD_CFI_ADDR);
+ sizep = (Elf_Size *)preload_search_info(preload_kmdp,
+ MODINFO_METADATA | MODINFOMD_CFI_SIZE);
+ if (addrp != NULL && sizep != NULL) {
+ linker_kernel_file->kcfi_traps_addr = ef->address +
+ *addrp;
+ linker_kernel_file->kcfi_traps_size = *sizep;
}
}
(void)link_elf_preload_parse_symbols(ef);
@@ -893,6 +901,22 @@
#endif /* __arm__ */
+static void
+link_elf_locate_kcfi_traps_preload(struct linker_file *lf, caddr_t modptr)
+{
+ Elf_Addr *kcfi_addrp;
+ Elf_Size *kcfi_sizep;
+
+ kcfi_addrp = (Elf_Addr *)preload_search_info(modptr,
+ MODINFO_METADATA | MODINFOMD_CFI_ADDR);
+ kcfi_sizep = (Elf_Size *)preload_search_info(modptr,
+ MODINFO_METADATA | MODINFOMD_CFI_SIZE);
+ if (kcfi_addrp != NULL && kcfi_sizep != NULL) {
+ lf->kcfi_traps_addr = ((elf_file_t)lf)->address + *kcfi_addrp;
+ lf->kcfi_traps_size = *kcfi_sizep;
+ }
+}
+
static int
link_elf_link_preload(linker_class_t cls, const char *filename,
linker_file_t *result)
@@ -950,6 +974,8 @@
link_elf_locate_exidx_preload(lf, modptr);
#endif
+ link_elf_locate_kcfi_traps_preload(lf, modptr);
+
error = parse_dynamic(ef);
if (error == 0)
error = parse_dpcpu(ef);
@@ -1306,11 +1332,21 @@
if (shdr[i].sh_type == SHT_SYMTAB) {
symtabindex = i;
symstrindex = shdr[i].sh_link;
- } else if (shstrs != NULL && shdr[i].sh_name != 0 &&
- strcmp(shstrs + shdr[i].sh_name, ".ctors") == 0) {
- /* Record relocated address and size of .ctors. */
- lf->ctors_addr = mapbase + shdr[i].sh_addr - base_vaddr;
- lf->ctors_size = shdr[i].sh_size;
+ } else if (shstrs != NULL && shdr[i].sh_name != 0) {
+ if (strcmp(shstrs + shdr[i].sh_name, ".ctors") == 0) {
+ /*
+ * Record relocated address and size of .ctors.
+ */
+ lf->ctors_addr = mapbase + shdr[i].sh_addr -
+ base_vaddr;
+ lf->ctors_size = shdr[i].sh_size;
+ }
+ else if (strcmp(shstrs + shdr[i].sh_name,
+ ".kcfi_traps") == 0) {
+ lf->kcfi_traps_addr = mapbase +
+ shdr[i].sh_addr - base_vaddr;
+ lf->kcfi_traps_size = shdr[i].sh_size;
+ }
}
}
if (symtabindex < 0 || symstrindex < 0)
@@ -1680,7 +1716,7 @@
static int
link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
- linker_symval_t *symval, bool see_local)
+ linker_symval_t *symval, bool see_local) __nosanitizekcfi
{
elf_file_t ef;
const Elf_Sym *es;
@@ -1716,7 +1752,7 @@
static int
link_elf_debug_symbol_values(linker_file_t lf, c_linker_sym_t sym,
- linker_symval_t *symval)
+ linker_symval_t *symval) __nosanitizekcfi
{
elf_file_t ef = (elf_file_t)lf;
const Elf_Sym *es = (const Elf_Sym *)sym;
@@ -2048,7 +2084,7 @@
*/
static int
elf_lookup_ifunc(linker_file_t lf, Elf_Size symidx, int deps __unused,
- Elf_Addr *res)
+ Elf_Addr *res) __nosanitizekcfi
{
elf_file_t ef;
const Elf_Sym *symp;
diff --git a/sys/kern/link_elf_obj.c b/sys/kern/link_elf_obj.c
--- a/sys/kern/link_elf_obj.c
+++ b/sys/kern/link_elf_obj.c
@@ -575,7 +575,11 @@
lf->dtors_size = shdr[i].sh_size;
}
}
-
+ else if (ef->progtab[pb].name != NULL &&
+ strcmp(ef->progtab[pb].name, ".kcfi_traps") == 0) {
+ lf->kcfi_traps_addr = ef->progtab[pb].addr;
+ lf->kcfi_traps_size = shdr[i].sh_size;
+ }
/* Update all symbol values with the offset. */
for (j = 0; j < ef->ddbsymcnt; j++) {
es = &ef->ddbsymtab[j];
@@ -642,7 +646,7 @@
}
static void
-link_elf_invoke_cbs(caddr_t addr, size_t size)
+link_elf_invoke_cbs(caddr_t addr, size_t size) __nosanitizekcfi
{
void (**ctor)(void);
size_t i, cnt;
@@ -1080,6 +1084,11 @@
shdr[i].sh_size;
}
}
+ else if (!strcmp(ef->progtab[pb].name,
+ ".kcfi_traps")) {
+ lf->kcfi_traps_addr = (caddr_t)mapbase;
+ lf->kcfi_traps_size = shdr[i].sh_size;
+ }
} else if (shdr[i].sh_type == SHT_PROGBITS)
ef->progtab[pb].name = "<<PROGBITS>>";
#ifdef __amd64__
@@ -1554,7 +1563,7 @@
static int
link_elf_symbol_values1(linker_file_t lf, c_linker_sym_t sym,
- linker_symval_t *symval, bool see_local)
+ linker_symval_t *symval, bool see_local) __nosanitizekcfi
{
elf_file_t ef;
const Elf_Sym *es;
@@ -1731,7 +1740,8 @@
* the case that the symbol can be found through the hash table.
*/
static int
-elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps, Elf_Addr *res)
+elf_obj_lookup(linker_file_t lf, Elf_Size symidx, int deps,
+ Elf_Addr *res) __nosanitizekcfi
{
elf_file_t ef = (elf_file_t)lf;
Elf_Sym *sym;
diff --git a/sys/kern/subr_cfi.c b/sys/kern/subr_cfi.c
new file mode 100644
--- /dev/null
+++ b/sys/kern/subr_cfi.c
@@ -0,0 +1,126 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 ShengYi Hung
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/systm.h>
+#include <sys/linker.h>
+#include <sys/priv.h>
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+
+#define CFI_DEBUG_MSG "CFI check failed on PC %p for callee %p and type %x\n"
+
+FEATURE(kcfi, "Kernel control flow integrity");
+
+static SYSCTL_NODE(_debug, OID_AUTO, kcfi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
+ "KCFI options");
+
+static bool cfi_panic __read_mostly = false;
+SYSCTL_BOOL(_debug_kcfi, OID_AUTO, panic_on_violation,
+ CTLFLAG_RDTUN | CTLFLAG_NOFETCH, &cfi_panic, 0, "Panic on KCFI violation");
+
+static bool cfi_disabled __read_mostly = false;
+SYSCTL_BOOL(_debug_kcfi, OID_AUTO, disabled, CTLFLAG_RDTUN | CTLFLAG_NOFETCH,
+ &cfi_disabled, 0, "Disable KCFI message");
+
+static void
+report_cfi(uintptr_t caller_addr, uintptr_t callee_addr, uint32_t type)
+{
+ if (cfi_panic)
+ panic(CFI_DEBUG_MSG, (void *)caller_addr, (void *)callee_addr,
+ type);
+ else
+ printf(CFI_DEBUG_MSG, (void *)caller_addr, (void *)callee_addr,
+ type);
+}
+
+static int
+kcfi_lookup_module(linker_file_t lf, void *arg)
+{
+ uintptr_t *module = (uintptr_t *)arg;
+
+ if (lf->address <= (caddr_t)*module &&
+ (lf->address + lf->size) >= (caddr_t)*module) {
+ *module = (uintptr_t)lf;
+ return (1);
+ }
+
+ return (0);
+}
+
+static inline uintptr_t
+cfi_trap_addr(int32_t *addr)
+{
+ return ((uintptr_t)((int32_t)(intptr_t)addr + *addr));
+}
+
+static bool
+is_cfi_exception(uintptr_t address)
+{
+ linker_file_t module;
+ int32_t *trap, *traps_end;
+
+ module = (linker_file_t)address;
+ linker_file_foreach(kcfi_lookup_module, &module);
+
+ if ((uintptr_t)module == address)
+ return (false);
+
+ trap = (int32_t *)(module->kcfi_traps_addr);
+ traps_end = (int32_t *)(module->kcfi_traps_addr +
+ module->kcfi_traps_size);
+
+ if (trap == NULL)
+ return (false);
+
+ for (; trap != traps_end; trap++)
+ if (cfi_trap_addr(trap) == address)
+ return (true);
+
+ return (false);
+}
+
+bool
+cfi_handler(struct trapframe *frame)
+{
+ uintptr_t callee_addr, caller_addr;
+ uint32_t type;
+
+ caller_addr = TRAPF_PC(frame);
+
+ if (decode_cfi_frame(frame, &callee_addr, &type) &&
+ is_cfi_exception((uintptr_t)caller_addr)) {
+ report_cfi(caller_addr, callee_addr, type);
+ /* needs to be platform dependent */
+ post_cfi(frame);
+ return (true);
+ }
+
+ return (false);
+}
+
diff --git a/sys/kern/subr_module.c b/sys/kern/subr_module.c
--- a/sys/kern/subr_module.c
+++ b/sys/kern/subr_module.c
@@ -406,6 +406,12 @@
case MODINFOMD_KEYBUF:
sbuf_cat(sbp, "MODINFOMD_KEYBUF");
break;
+ case MODINFOMD_CFI_ADDR:
+ sbuf_cat(sbp, "MODINFOMD_CFI_ADDR");
+ break;
+ case MODINFOMD_CFI_SIZE:
+ sbuf_cat(sbp, "MODINFOMD_CFI_SIZE");
+ break;
#ifdef MODINFOMD_SMAP
case MODINFOMD_SMAP:
sbuf_cat(sbp, "MODINFOMD_SMAP");
@@ -488,6 +494,7 @@
break;
case MODINFO_SIZE:
case MODINFO_METADATA | MODINFOMD_CTORS_SIZE:
+ case MODINFO_METADATA | MODINFOMD_CFI_SIZE:
sbuf_printf(sbp, "%lu", *(u_long *)bptr);
break;
case MODINFO_ADDR:
@@ -497,6 +504,7 @@
case MODINFO_METADATA | MODINFOMD_KERNEND:
case MODINFO_METADATA | MODINFOMD_ENVP:
case MODINFO_METADATA | MODINFOMD_CTORS_ADDR:
+ case MODINFO_METADATA | MODINFOMD_CFI_ADDR:
#ifdef MODINFOMD_SMAP
case MODINFO_METADATA | MODINFOMD_SMAP:
#endif
diff --git a/sys/sys/cdefs.h b/sys/sys/cdefs.h
--- a/sys/sys/cdefs.h
+++ b/sys/sys/cdefs.h
@@ -617,6 +617,11 @@
#else
#define __nosanitizethread
#endif
+#if __has_feature(kcfi) && defined(__clang__)
+#define __nosanitizekcfi __attribute__((no_sanitize("kcfi")))
+#else
+#define __nosanitizekcfi
+#endif
/*
* Make it possible to opt out of stack smashing protection.
diff --git a/sys/sys/linker.h b/sys/sys/linker.h
--- a/sys/sys/linker.h
+++ b/sys/sys/linker.h
@@ -104,6 +104,8 @@
*/
int nenabled; /* number of enabled probes. */
int fbt_nentries; /* number of fbt entries created. */
+ caddr_t kcfi_traps_addr; /* KCFI trap table start */
+ size_t kcfi_traps_size; /* KCFI trap table size */
#ifdef __arm__
caddr_t exidx_addr; /* Unwind data index table start */
@@ -260,6 +262,8 @@
#define MODINFOMD_FONT 0x000e /* Console font */
#define MODINFOMD_SPLASH 0x000f /* Console splash screen */
#define MODINFOMD_SHTDWNSPLASH 0x0010 /* Console shutdown splash screen */
+#define MODINFOMD_CFI_ADDR 0x0020 /* address of .kcfi_traps */
+#define MODINFOMD_CFI_SIZE 0x0040 /* size of .kcfi_traps */
#define MODINFOMD_NOCOPY 0x8000 /* don't copy this metadata to the kernel */
#define MODINFOMD_DEPLIST (0x4001 | MODINFOMD_NOCOPY) /* depends on */
diff --git a/sys/sys/systm.h b/sys/sys/systm.h
--- a/sys/sys/systm.h
+++ b/sys/sys/systm.h
@@ -602,6 +602,15 @@
#define __witness_used __unused
#endif
+#ifdef KCFI
+
+bool cfi_handler(struct trapframe *);
+bool decode_cfi_frame(struct trapframe *, uintptr_t *, uint32_t *);
+void post_cfi(struct trapframe *);
+
+#endif
+
+
#endif /* _KERNEL */
__NULLABILITY_PRAGMA_POP

File Metadata

Mime Type
text/plain
Expires
Fri, Jul 10, 7:57 PM (3 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34939116
Default Alt Text
D46193.diff (21 KB)

Event Timeline