summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/include/sys/limits.h2
-rw-r--r--sys/include/sys/proc.h3
-rw-r--r--sys/include/sys/vsr.h163
-rw-r--r--sys/kern/kern_vsr.c360
4 files changed, 528 insertions, 0 deletions
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 <sys/syscall.h>
#include <sys/exec.h>
#include <sys/ucred.h>
+#include <sys/limits.h>
+#include <sys/vsr.h>
#include <sys/filedesc.h>
#include <sys/signal.h>
#include <sys/vnode.h>
@@ -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 <sys/types.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/limits.h>
+#if defined(_KERNEL)
+#include <sys/mutex.h>
+#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 <sys/vsr.h>
+#include <sys/proc.h>
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/syslog.h>
+#include <vm/dynalloc.h>
+#include <string.h>
+
+#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);
+ }
+}