Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F162105632
D46193.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D46193.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D46193: sys: implement Kernel CFI from clang
Attached
Detach File
Event Timeline
Log In to Comment