diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index a698ebbed9ef..bc0a46787847 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -148,6 +148,8 @@ const char *ld_compartment_stats; /* Export count of compartment switches to statistics */ const char *ld_compartment_switch_count; +/* Compartmentalisation information exported to the kernel */ +static struct cheri_c18n_info *c18n_info; struct rtld_c18n_stats *c18n_stats; #define INC_NUM_COMPART (c18n_stats->rcs_compart++, comparts.size++) @@ -242,7 +244,8 @@ string_base_search(const struct string_base *sb, const char *str) struct compart { /* - * Name of the compartment + * Name of the compartment. Must be the first field to enable kernel + * access to compartment information. */ const char *name; /* @@ -296,9 +299,15 @@ expand_comparts_data(compart_id_t capacity) { struct compart *data; + atomic_fetch_add_explicit(&c18n_info->comparts_gen, 1, + memory_order_acq_rel); + data = c18n_realloc(comparts.data, sizeof(*data) * capacity); - comparts.data = r_debug.r_comparts = data; + comparts.data = c18n_info->comparts = r_debug.r_comparts = data; comparts.capacity = capacity; + + atomic_fetch_add_explicit(&c18n_info->comparts_gen, 1, + memory_order_acq_rel); } static struct compart * @@ -312,12 +321,18 @@ add_comparts_data(const char *name) if (comparts.size == comparts.capacity) expand_comparts_data(comparts.capacity * 2); + atomic_fetch_add_explicit(&c18n_info->comparts_gen, 1, + memory_order_acq_rel); GDB_COMPARTS_STATE(RCT_ADD, NULL); + com = &comparts.data[INC_NUM_COMPART]; *com = (struct compart) { .name = name }; - r_debug.r_comparts_size = comparts.size; + c18n_info->comparts_size = r_debug.r_comparts_size = comparts.size; + + atomic_fetch_add_explicit(&c18n_info->comparts_gen, 1, + memory_order_acq_rel); GDB_COMPARTS_STATE(RCT_CONSISTENT, com); return (com); @@ -1590,7 +1605,6 @@ c18n_init(Obj_Entry *obj_rtld, Elf_Auxinfo *aux_info[]) int fd; char *file; struct stat st; - struct cheri_c18n_info *info; /* * Create memory mapping for compartmentalisation statistics. @@ -1616,13 +1630,14 @@ c18n_init(Obj_Entry *obj_rtld, Elf_Auxinfo *aux_info[]) memory_order_release); if (aux_info[AT_CHERI_C18N] != NULL) { - info = aux_info[AT_CHERI_C18N]->a_un.a_ptr; - *info = (struct cheri_c18n_info) { + c18n_info = aux_info[AT_CHERI_C18N]->a_un.a_ptr; + *c18n_info = (struct cheri_c18n_info) { .stats_size = sizeof(*c18n_stats), - .stats = c18n_stats + .stats = c18n_stats, + .comparts_entry_size = sizeof(*comparts.data) }; - atomic_store_explicit(&info->version, CHERI_C18N_INFO_VERSION, - memory_order_release); + atomic_store_explicit(&c18n_info->version, + CHERI_C18N_INFO_VERSION, memory_order_release); } /* diff --git a/sys/cheri/c18n.h b/sys/cheri/c18n.h index 69ae14c4c986..fe1e4cd0b64c 100644 --- a/sys/cheri/c18n.h +++ b/sys/cheri/c18n.h @@ -2,6 +2,12 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 Dapeng Gao + * Copyright (c) 2024 Capabilities Limited + * + * This software was developed by SRI International, the University of + * Cambridge Computer Laboratory (Department of Computer Science and + * Technology), and Capabilities Limited under Defense Advanced Research + * Projects Agency (DARPA) Contract No. FA8750-24-C-B047 ("DEC"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -44,7 +50,7 @@ * initialised. */ struct rtld_c18n_stats { - _Atomic(uint8_t) version; + _Atomic(size_t) version; size_t rcs_compart; _Atomic(size_t) rcs_ustack; _Atomic(size_t) rcs_tramp; @@ -58,12 +64,25 @@ struct rtld_c18n_stats { * information. The version field doubles as a synchronisation flag where a * non-zero value indicates that the other fields have been initialised. */ -#define CHERI_C18N_INFO_VERSION 1 +#define CHERI_C18N_INFO_VERSION 2 struct cheri_c18n_info { - _Atomic(uint8_t) version; + _Atomic(size_t) version; + size_t stats_size; struct rtld_c18n_stats * __kerncap stats; + + /* + * Since the `comparts` array may be reallocated or ortherwise change + * whilst the kernel is reading it, the generation counter allows the + * kernel to identify such races. An even value indicates that the + * array and size data are in a consistent state, and an odd value + * indicates that the data may be inconsistent. + */ + _Atomic(size_t) comparts_gen; + size_t comparts_size; + size_t comparts_entry_size; + void * __kerncap comparts; }; #ifndef IN_RTLD