From 15c4ee1321be918d8fcbc8d858ea90a4dabe54a5 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Sat, 26 Jul 2025 02:10:13 -0400 Subject: kernel: Introduce initial VSR impl This commit introduces the initial implementation of Virtual System Resources (VSRs). Virtual system resources enable specific subsystems to control global state while the VSR acts as a middle man between the process, and the actual resource itself. This allows the products of certain operations to be routed to either the global state or a local (per-process) "shallow copy" whose data and side effects are only visible through that process. An example of something this would be useful for is some sort of transparent access control mechanism where if a process should not modify global system-wide state, it can at least modify the state for itself. Signed-off-by: Ian Moffett --- sys/include/sys/limits.h | 2 + sys/include/sys/proc.h | 3 + sys/include/sys/vsr.h | 163 +++++++++++++++++++++ sys/kern/kern_vsr.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 528 insertions(+) create mode 100644 sys/include/sys/vsr.h create mode 100644 sys/kern/kern_vsr.c diff --git a/sys/include/sys/limits.h b/sys/include/sys/limits.h index f6aed9d..963d113 100644 --- a/sys/include/sys/limits.h +++ b/sys/include/sys/limits.h @@ -36,4 +36,6 @@ #define ARG_MAX 4096 #define CHAR_BIT 8 #define CPU_MAX 256 +#define VSR_MAX_DOMAIN 16 +#define VSR_MAX_CAPSULE 16 #endif /* !_SYS_LIMITS_H_ */ diff --git a/sys/include/sys/proc.h b/sys/include/sys/proc.h index 9cc9238..89fe638 100644 --- a/sys/include/sys/proc.h +++ b/sys/include/sys/proc.h @@ -39,6 +39,8 @@ #include #include #include +#include +#include #include #include #include @@ -88,6 +90,7 @@ struct proc { struct ucred cred; struct ksiginfo *ksig_list[PROC_SIGMAX]; struct filedesc *fds[PROC_MAX_FILEDES]; + struct vsr_domain *vsr_tab[VSR_MAX_DOMAIN]; struct mmap_lgdr *mlgdr; struct vcache *vcache; struct spinlock vcache_lock; diff --git a/sys/include/sys/vsr.h b/sys/include/sys/vsr.h new file mode 100644 index 0000000..88cb659 --- /dev/null +++ b/sys/include/sys/vsr.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * 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. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + */ + +#ifndef _SYS_VSR_H_ +#define _SYS_VSR_H_ + +#include +#include +#include +#include +#include +#if defined(_KERNEL) +#include +#endif /* _KERNEL */ + +#define VSR_FILE 0x00000000 /* Represented by file */ + +/* + * Defines the access semantics of whether + * r/w operations should be passed down to the + * global state or soley affecting a per-process + * shallow copy. + */ +typedef uint32_t vsr_mode_t; + +/* + * The Virtual System Resource namespace consists of + * domains containing named "capsules". The domain is + * simply a table indexed by a type value e.g. VSR_FILE + * and a capsule is simply a structure containing global data + * as well as a shallow copy which is controlled locally by the + * process. The capsule also contains various access semantics + * that help the VSR subsystem determine whether the access should + * be passed down globally or virtualized locally within the process. + */ +typedef uint8_t vsr_domain_t; + +/* + * VSR mode bits + */ +#define VSR_GLOB_WRITE BIT(0) /* Writes are global */ +#define VSR_GLOB_READ BIT(1) /* Reads are global */ +#define VSR_GLOB_CRED BIT(2) /* Global for specific creds */ + +#if defined(_KERNEL) + +struct vsr_capsule; + +/* + * VSR capsule operations + * + * @reclaim: Cleanup resources + */ +struct capsule_ops { + int(*reclaim)(struct vsr_capsule *cap, int flags); +}; + +/* + * Virtual system resource access + * semantics. + * + * @glob: Global data + * @shallow: Local per process copy + * @mode: VSR mode (see VSR_GLOB_*) + * @cred: Creds (used if VSR_GLOBAL_CRED set) + */ +struct vsr_access { + void *glob; + void *shallow; + vsr_mode_t mode; + struct ucred cred; +}; + +/* + * A virtual system resource capsule containing + * resource owner specific data and hashmap + * buckets. + * + * @name: Capsule name (e.g., "consfeat"), must be freed + * @data: Owner specific data + * @shadow: Local shadow copy (per-process) + * @buckets: Hashmap buckets + * @link: Bucket link + * @ops: Capsule operations + * @lock: Mutex lock protecting fields + */ +struct vsr_capsule { + char *name; + void *data; + void *shadow; + TAILQ_HEAD(, vsr_capsule) buckets; + TAILQ_ENTRY(vsr_capsule) link; + struct capsule_ops ops; + struct mutex lock; +}; + +/* + * Virtual system resource table containg + * VSRs for various types. + * + * Each VSR table belongs to a VSR domain + * (e.g., VSR_FILE). + * + * @ncaps: Number of capsules + * @is_init: Set if hashmap is set up + * @capsules: VSR capsule hashmap + */ +struct vsr_table { + struct vsr_capsule *capsules[VSR_MAX_CAPSULE]; +}; + +/* + * Virtual system resource domain (VSR). + * + * A VSR is represented by a specific VSR type + * (see VSR_*). Each VSR has a table of VSR capsules + * looked up by a VSR capsule name. + * + * One per process. + * + * @type: VSR type + * @table: VSR table + */ +struct vsr_domain { + int type; + struct vsr_table table; +}; + +void vsr_init_domains(void); +void vsr_destroy_domains(void); + +struct vsr_domain *vsr_new_domain(vsr_domain_t type); +struct vsr_capsule *vsr_new_capsule(vsr_domain_t type, const char *name); +struct vsr_capsule *vsr_lookup_capsule(vsr_domain_t type, const char *name); + +#endif /* _KERNEL */ +#endif /* !_SYS_VSR_H_ */ diff --git a/sys/kern/kern_vsr.c b/sys/kern/kern_vsr.c new file mode 100644 index 0000000..8fb7fc7 --- /dev/null +++ b/sys/kern/kern_vsr.c @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. + * 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. + * 3. Neither the name of Hyra nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 +#include +#include +#include +#include +#include +#include + +#define pr_trace(fmt, ...) kprintf("vsr: " fmt, ##__VA_ARGS__) +#define pr_error(...) pr_trace(__VA_ARGS__) + +static uint32_t +fnv1_hash(const char *s) +{ + uint32_t hash = 2166136261UL; + const uint8_t *p = (uint8_t *)s; + + while (*p != '\0') { + hash ^= *p; + hash = hash * 0x01000193; + ++p; + } + + return hash; +} + +/* + * Add a VSR capsule to a domain. + */ +static void +vsr_domain_add(struct vsr_domain *vsp, struct vsr_capsule *cap) +{ + struct vsr_table *tab; + struct vsr_capsule **slot; + uint32_t hash; + + if (vsp == NULL || cap == NULL) { + return; + } + + if (cap->name == NULL) { + pr_error("vsr_domain_add: cap->name == NULL\n"); + return; + } + + tab = &vsp->table; + hash = fnv1_hash(cap->name); + slot = &tab->capsules[hash % VSR_MAX_CAPSULE]; + + /* If this slot is free, set it */ + if (*slot == NULL) { + *slot = cap; + return; + } + + /* Handle collision */ + TAILQ_INSERT_TAIL(&(*slot)->buckets, cap, link); +} + +/* + * Handle VSR domain hashmap collisions. + * + * @slot: Slot that we have collided with + * @name: Name to lookup + * + * Returns the pointer to the actual capsule if the + * collision has been resolved, otherwise, NULL if the + * entry to look up was not found. + */ +static struct vsr_capsule * +vsr_domain_clash(struct vsr_capsule *slot, const char *name) +{ + struct vsr_capsule *cap_ent; + + TAILQ_FOREACH(cap_ent, &slot->buckets, link) { + if (cap_ent == NULL) { + continue; + } + + if (strcmp(cap_ent->name, name) == 0) { + return cap_ent; + } + } + + return NULL; +} + +/* + * Lookup a capsule within a VSR domain + * by name. + * + * @vsp: Domain to lookup within + * @name: Name to use as lookup key + * + * Returns NULL if no entry was found. + */ +static struct vsr_capsule * +vfs_domain_lookup(struct vsr_domain *vsp, const char *name) +{ + uint32_t hash; + struct vsr_table *tab; + struct vsr_capsule **slot; + + if (vsp == NULL || name == NULL) { + return NULL; + } + + tab = &vsp->table; + hash = fnv1_hash(name); + slot = &tab->capsules[hash % VSR_MAX_CAPSULE]; + + if (*slot == NULL) { + return NULL; + } + + if (strcmp((*slot)->name, name) != 0) { + return vsr_domain_clash(*slot, name); + } + + return *slot; +} + +/* + * Destroy a VSR capsule + * + * @capule: Capsule to destroy + */ +static void +vsr_destroy_capsule(struct vsr_capsule *capsule) +{ + struct vsr_capsule *bucket; + struct capsule_ops *ops; + + if (capsule->name != NULL) { + dynfree(capsule->name); + capsule->name = NULL; + } + + ops = &capsule->ops; + if (ops->reclaim != NULL) { + ops->reclaim(capsule, 0); + } + + TAILQ_FOREACH(bucket, &capsule->buckets, link) { + if (bucket == NULL) { + continue; + } + vsr_destroy_capsule(bucket); + } + + /* Release any held locks */ + mutex_release(&capsule->lock); +} + +/* + * Destroy a VSR table + * + * @tab: Table to destroy. + */ +static void +vsr_destroy_table(struct vsr_table *tab) +{ + struct vsr_capsule *capsule; + + if (tab == NULL) { + pr_error("vsr_destroy_table: tab is NULL\n"); + return; + } + + for (int i = 0; i < VSR_MAX_CAPSULE; ++i) { + if ((capsule = tab->capsules[i]) == NULL) { + continue; + } + + vsr_destroy_capsule(capsule); + } +} + +/* + * Allocate a new VSR capsule and add it to + * VSR domain. + */ +struct vsr_capsule * +vsr_new_capsule(vsr_domain_t type, const char *name) +{ + struct vsr_capsule *capsule; + struct vsr_domain *domain; + struct proc *td = this_td(); + + /* Valid type? */ + if (type >= VSR_MAX_DOMAIN) { + return NULL; + } + + if (__unlikely(td == NULL)) { + return NULL; + } + + /* + * The VSR domain must be registered for + * us to add any capsules to it. + */ + if ((domain = td->vsr_tab[type]) == NULL) { + pr_error("VSR domain %d not registered\n", type); + return NULL; + } + + /* Allocate a new capsule */ + capsule = dynalloc(sizeof(*capsule)); + if (capsule == NULL) { + return NULL; + } + + memset(capsule, 0, sizeof(*capsule)); + capsule->name = strdup(name); + + TAILQ_INIT(&capsule->buckets); + vsr_domain_add(domain, capsule); + return capsule; +} + +/* + * Allocate a new VSR domain and add it to + * the current process. + * + * @type: VSR type (e.g., VSR_FILE) + */ +struct vsr_domain * +vsr_new_domain(vsr_domain_t type) +{ + struct vsr_domain *domain; + struct vsr_table *tablep; + struct proc *td = this_td(); + + /* Valid type? */ + if (type >= VSR_MAX_DOMAIN) { + return NULL; + } + + /* + * The scheduler should be set up before any + * calls to vsr_new_vec() should be made. + */ + if (__unlikely(td == NULL)) { + return NULL; + } + + /* + * Do not overwrite the entry if it is + * already allocated and log this anomalous + * activity. + */ + if (td->vsr_tab[type] != NULL) { + pr_error("[security]: type %d already allocated\n", type); + return NULL; + } + + domain = dynalloc(sizeof(*domain)); + if (domain == NULL) { + return NULL; + } + + /* Initialize the domain */ + memset(domain, 0, sizeof(*domain)); + domain->type = type; + + /* Initialize the domain's table */ + tablep = &domain->table; + td->vsr_tab[type] = domain; + return domain; +} + +/* + * Lookup a capsule by name for the current + * process. + */ +struct vsr_capsule * +vsr_lookup_capsule(vsr_domain_t type, const char *name) +{ + struct vsr_domain *domain; + struct proc *td = this_td(); + + /* Must be on a process */ + if (__unlikely(td == NULL)) { + return NULL; + } + + /* + * The VSR domain must be registered for + * us to lookup any capsules from it. + */ + if ((domain = td->vsr_tab[type]) == NULL) { + pr_error("VSR domain %d not registered\n", type); + return NULL; + } + + return vfs_domain_lookup(domain, name); +} + +/* + * Initialize per-process domains + */ +void +vsr_init_domains(void) +{ + if (vsr_new_domain(VSR_FILE) == NULL) { + pr_error("failed to initialize VSR file domain\n"); + } +} + +/* + * Destroy per-process domains + */ +void +vsr_destroy_domains(void) +{ + struct proc *td = this_td(); + struct vsr_domain *domain; + + if (__unlikely(td == NULL)) { + return; + } + + for (int i = 0; i < VSR_MAX_DOMAIN; ++i) { + if ((domain = td->vsr_tab[i]) == NULL) { + continue; + } + + vsr_destroy_table(&domain->table); + } +} -- cgit v1.2.3