Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c18n: Allow system calls only for code with CHERI_PERM_SYSCALL and secure mmap #2320

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions bin/cheribsdtest/cheribsdtest_registers.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
static void
check_initreg_code(void * __capability c)
{
uintmax_t v;
uintmax_t v, expect;

#if defined(__CHERI_PURE_CAPABILITY__) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI)
/*
Expand Down Expand Up @@ -165,22 +165,24 @@
if ((v & CHERI_PERM_SYSTEM_REGS) != 0)
cheribsdtest_failure_errx("perms %jx (system_regs present)", v);

if ((v & CHERI_PERMS_SWALL) !=
(CHERI_PERMS_SWALL & ~CHERI_PERM_SW_VMEM))
cheribsdtest_failure_errx("swperms %jx (expected swperms %x)",
v & CHERI_PERMS_SWALL,
(CHERI_PERMS_SWALL & ~CHERI_PERM_SW_VMEM));
expect = CHERI_PERMS_SWALL & ~CHERI_PERM_SW_VMEM;
#ifdef CHERIBSD_C18N_TESTS
expect &= ~CHERI_PERM_SYSCALL;
#endif
if ((v & CHERI_PERMS_SWALL) != expect)
cheribsdtest_failure_errx("swperms %jx (expected swperms %jx)",
v & CHERI_PERMS_SWALL, expect);

/* Check that the raw permission bits match the kernel header: */
#if defined(CHERIBSD_C18N_TESTS) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI)
if (v != (CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE))
cheribsdtest_failure_errx("perms %jx (expected %jx)", v,
(uintmax_t)(CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE));
#else
if (v != CHERI_CAP_USER_CODE_PERMS)
cheribsdtest_failure_errx("perms %jx (expected %jx)", v,
(uintmax_t)CHERI_CAP_USER_CODE_PERMS);
expect = CHERI_CAP_USER_CODE_PERMS;
#ifdef CHERIBSD_C18N_TESTS
expect &= ~CHERI_PERM_SYSCALL;
#if defined(__aarch64__) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI)
expect &= ~CHERI_PERM_EXECUTIVE;
#endif
#endif
if (v != expect)
cheribsdtest_failure_errx("perms %jx (expected %jx)", v, expect);

Check warning on line 185 in bin/cheribsdtest/cheribsdtest_registers.c

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters

Check warning on line 185 in bin/cheribsdtest/cheribsdtest_registers.c

View workflow job for this annotation

GitHub Actions / Style Checker

line over 80 characters

cheribsdtest_success();
}
Expand Down
2 changes: 2 additions & 0 deletions lib/libsys/Makefile.sys
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ INTERPOSED = \
.ifdef CHERI_LIB_C18N
# Pseudo implementations of system calls we don't currently fully support
SRCS+= \
mmap_c18n.c \
rfork_c18n.c \
thr_exit_c18n.c \
vfork_c18n.c

PSEUDO+= \
mmap \
rfork
NOASM+= \
thr_exit.o \
Expand Down
45 changes: 45 additions & 0 deletions lib/libsys/mmap_c18n.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 Dapeng Gao
*
* 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/mman.h>

#include <cheri/cheric.h>

#include "libc_private.h"

void *
mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset)
{
void *ret;

ret = __sys_mmap(addr, len, prot, flags, fd, offset);
#if defined(__aarch64__) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI)
ret = cheri_clearperm(ret, CHERI_PERM_EXECUTIVE);
#endif
ret = cheri_clearperm(ret, CHERI_PERM_SYSCALL);
return (ret);
}
3 changes: 0 additions & 3 deletions libexec/rtld-elf/map_object.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,6 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat
base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags);
mapbase = mmap(base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL),
base_flags, -1, 0);
#ifdef CHERI_LIB_C18N
mapbase = cheri_clearperm(mapbase, c18n_code_perm_clear);
#endif
if (mapbase == MAP_FAILED) {
_rtld_error("%s: mmap of entire address space failed: %s",
path, rtld_strerror(errno));
Expand Down
19 changes: 15 additions & 4 deletions libexec/rtld-elf/rtld.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,6 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
ld_elf_hints_default = _PATH_ELF_HINTS_C18N;
ld_path_libmap_conf = _PATH_LIBMAP_CONF_C18N;
ld_standard_library_path = STANDARD_LIBRARY_PATH_C18N;
c18n_code_perm_clear = CHERI_PERM_EXECUTIVE;
}
#endif

Expand Down Expand Up @@ -884,9 +883,6 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp)
assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr));
assert(aux_info[AT_ENTRY] != NULL);
imgentry = (dlfunc_t) aux_info[AT_ENTRY]->a_un.a_ptr;
#ifdef CHERI_LIB_C18N
imgentry = (dlfunc_t) cheri_clearperm(imgentry, c18n_code_perm_clear);
#endif
dbg("Values from kernel:\n\tAT_PHDR=" PTR_FMT "\n"
"\tAT_BASE=" PTR_FMT "\n\tAT_ENTRY=" PTR_FMT "\n",
phdr, aux_info[AT_BASE]->a_un.a_ptr, (const void *)imgentry);
Expand Down Expand Up @@ -6404,6 +6400,15 @@ c18n_assign_plt_compartments(Obj_Entry *obj)
}
}

static const void *
c18n_clear_pcc_perms(const char *name, const void *pcc)
{
pcc = cheri_clearperm(pcc, CHERI_PERM_EXECUTIVE);
if (strcmp("libsys.so.7", name) != 0)
pcc = cheri_clearperm(pcc, CHERI_PERM_SYSCALL);
return (pcc);
}

static bool
c18n_add_obj(Obj_Entry *obj, const char *name)
{
Expand All @@ -6427,6 +6432,12 @@ c18n_add_obj(Obj_Entry *obj, const char *name)
return (false);
}

/* Reduce the permission of each compartment's pcc. */
obj->entry = c18n_clear_pcc_perms(name, obj->entry);
obj->text_rodata_cap = c18n_clear_pcc_perms(name, obj->text_rodata_cap);
for (unsigned long i = 0; i < obj->npcc_caps; ++i)
obj->pcc_caps[i] = c18n_clear_pcc_perms(name, obj->pcc_caps[i]);

c18n_setup_compartments(obj, name);
c18n_assign_plt_compartments(obj);
return (true);
Expand Down
3 changes: 0 additions & 3 deletions libexec/rtld-elf/rtld_c18n.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ uintptr_t sealer_pltgot, sealer_tramp;
/* Enable compartmentalisation */
bool ld_compartment_enable;

/* Permission bit to be cleared for user code */
uint64_t c18n_code_perm_clear;

/* Use utrace() to log compartmentalisation-related events */
const char *ld_compartment_utrace;

Expand Down
1 change: 0 additions & 1 deletion libexec/rtld-elf/rtld_c18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
/*
* Global symbols
*/
extern size_t c18n_code_perm_clear;
extern uintptr_t sealer_pltgot, sealer_tramp;
extern const char *ld_compartment_utrace;
extern const char *ld_compartment_policy;
Expand Down
5 changes: 5 additions & 0 deletions sys/arm64/arm64/trap.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ cpu_fetch_syscall_args(struct thread *td)

if (__predict_false(sa->code >= p->p_sysent->sv_size))
sa->callp = &nosys_sysent;
#if __has_feature(capabilities) && !defined(CPU_CHERI_NO_SYSCALL_AUTHORIZE)
/* Constrain code that can originate system calls. */
else if (__predict_false(!cheri_syscall_authorize(td)))
sa->callp = &nosys_sysent;
#endif
else
sa->callp = &p->p_sysent->sv_table[sa->code];

Expand Down
5 changes: 1 addition & 4 deletions sys/cheri/cheri.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ void * __capability cheri_sigcode_capability(struct thread *td);
* CHERI context management functions.
*/
const char *cheri_exccode_string(uint8_t exccode);
int cheri_syscall_authorize(struct thread *td, u_int code,
int nargs, syscallarg_t *args);
int cheri_syscall_authorize(struct thread *td);

/*
* Functions to manage object types.
Expand All @@ -164,8 +163,6 @@ bool vm_derive_capreg(struct proc *p, uintcap_t in, uintcap_t *out);
*/
SYSCTL_DECL(_security_cheri);
SYSCTL_DECL(_security_cheri_stats);
extern u_int security_cheri_debugger_on_sandbox_syscall;
extern u_int security_cheri_syscall_violations;
extern u_int security_cheri_bound_legacy_capabilities;
extern u_int cheri_cloadtags_stride;
#ifdef __aarch64__
Expand Down
40 changes: 4 additions & 36 deletions sys/cheri/cheri_syscall.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2011-2014, 2016 Robert N. M. Watson
* Copyright (c) 2025 Dapeng Gao
* All rights reserved.
*
* This software was developed by SRI International and the University of
Expand Down Expand Up @@ -28,50 +29,17 @@
* SUCH DAMAGE.
*/

#include "opt_ddb.h"

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/syscall.h>
#include <sys/sysctl.h>

#include <ddb/ddb.h>
#include <sys/kdb.h>

#include <cheri/cheri.h>
#include <cheri/cheric.h>

/*
* Only allow most system calls from either ambient authority, or from
* sandboxes that have been explicitly delegated CHERI_PERM_SYSCALL via their
* code capability. Note that CHERI_PERM_SYSCALL effectively implies ambient
* authority, as the kernel does not [currently] interpret pointers/lengths
* via userspace $ddc.
* Only allow system calls from code that has the CHERI_PERM_SYSCALL permission.
*/
int
cheri_syscall_authorize(struct thread *td, u_int code, int nargs,
syscallarg_t *args)
cheri_syscall_authorize(struct thread *td)
{
uintmax_t c_perms;

/*
* Check whether userspace holds the rights defined in
* cheri_capability_set_user() in $pcc. Note that object type doesn't
* come into play here.
*
* XXXRW: Possibly ECAPMODE should be EPROT or ESANDBOX?
*/
c_perms = cheri_getperm(__USER_PCC);
if ((c_perms & CHERI_PERM_SYSCALL) == 0) {
atomic_add_int(&security_cheri_syscall_violations, 1);

#ifdef DDB
if (security_cheri_debugger_on_sandbox_syscall)
kdb_enter(KDB_WHY_CHERI,
"Syscall rejected in CHERI sandbox");
#endif
return (ECAPMODE);
}
return (0);
return ((cheri_getperm(__USER_PCC) & CHERI_PERM_SYSCALL) != 0);
}
10 changes: 0 additions & 10 deletions sys/cheri/cheri_sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,10 @@ SYSCTL_UINT(_security_cheri, OID_AUTO, capability_size, CTLFLAG_RD,
SYSCTL_NODE(_security_cheri, OID_AUTO, stats, CTLFLAG_RD, 0,
"CHERI statistics");

/* XXXRW: Should possibly be u_long. */
u_int security_cheri_syscall_violations;
SYSCTL_UINT(_security_cheri_stats, OID_AUTO, syscall_violations, CTLFLAG_RD,
&security_cheri_syscall_violations, 0, "Number of system calls blocked");

/*
* A set of sysctls that cause the kernel debugger to enter following a policy
* violation or signal delivery due to CHERI or while in a sandbox.
*/
u_int security_cheri_debugger_on_sandbox_syscall;
SYSCTL_UINT(_security_cheri, OID_AUTO, debugger_on_sandbox_syscall, CTLFLAG_RW,
&security_cheri_debugger_on_sandbox_syscall, 0,
"Enter KDB when a syscall is rejected while in a sandbox");

u_int security_cheri_abort_on_memcpy_tag_loss;
SYSCTL_UINT(_security_cheri, OID_AUTO, abort_on_memcpy_tag_loss,
CTLFLAG_RW, &security_cheri_abort_on_memcpy_tag_loss, 0,
Expand Down
11 changes: 0 additions & 11 deletions sys/kern/subr_syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,9 @@
goto retval;
}

#if __has_feature(capabilities) && !defined(CPU_CHERI_NO_SYSCALL_AUTHORIZE)
/*
* Constrain code that can originate system calls if
* userspace sandboxing is available.
*/
error = cheri_syscall_authorize(td, sa->code, sa->callp->sy_narg,
sa->args);
if (error != 0)
goto retval;
#endif

if (__predict_false(traced)) {
PROC_LOCK(p);
if (p->p_ptevents & PTRACE_SCE)

Check warning on line 98 in sys/kern/subr_syscall.c

View workflow job for this annotation

GitHub Actions / Style Checker

Missing Signed-off-by: line

Check warning on line 98 in sys/kern/subr_syscall.c

View workflow job for this annotation

GitHub Actions / Style Checker

Missing Signed-off-by: line
ptracestop((td), SIGTRAP, NULL);
PROC_UNLOCK(p);

Expand Down