From e93f48e2f7d5fdcbb05cf86f3bdb1b91b63e39f3 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 14 Jan 2023 15:40:18 -0800 Subject: [PATCH] [kernel] Track capability reference counts First pass at reference-counting capabilities. --- src/kernel/capabilities.cpp | 41 ++++++++++++++++++++++++++++++- src/kernel/capabilities.h | 12 +++++++-- src/kernel/objects/process.cpp | 30 +++++++++++++++++----- src/kernel/objects/process.h | 2 +- src/kernel/syscall_verify.cpp.cog | 16 ++++++++++-- src/kernel/syscalls/helpers.h | 7 ++++-- 6 files changed, 94 insertions(+), 14 deletions(-) diff --git a/src/kernel/capabilities.cpp b/src/kernel/capabilities.cpp index f6789b6..1043678 100644 --- a/src/kernel/capabilities.cpp +++ b/src/kernel/capabilities.cpp @@ -20,6 +20,8 @@ cap_table::create(obj::kobject *target, j6_cap_t caps) { j6_handle_t new_handle = make_handle(m_count); + util::scoped_lock lock {m_lock}; + m_caps.insert({ new_handle, j6_handle_invalid, @@ -29,12 +31,17 @@ cap_table::create(obj::kobject *target, j6_cap_t caps) target, }); + if (target) + target->handle_retain(); + return new_handle; } j6_handle_t cap_table::derive(j6_handle_t base, j6_cap_t caps) { + util::scoped_lock lock {m_lock}; + capability *existing = m_caps.find(base); if (!existing) return j6_handle_invalid; @@ -52,11 +59,43 @@ cap_table::derive(j6_handle_t base, j6_cap_t caps) existing->object, }); + if (existing->object) + existing->object->handle_retain(); + return new_handle; } capability * -cap_table::find(j6_handle_t id) +cap_table::find_without_retain(j6_handle_t id) { return m_caps.find(id); } + +capability * +cap_table::retain(j6_handle_t id) +{ + util::scoped_lock lock {m_lock}; + + capability *cap = m_caps.find(id); + if (!cap) + return nullptr; + + ++cap->holders; + return cap; +} + +void +cap_table::release(j6_handle_t id) +{ + util::scoped_lock lock {m_lock}; + + capability *cap = m_caps.find(id); + if (!cap) + return; + + if (--cap->holders == 0) { + if (cap->object) + cap->object->handle_release(); + m_caps.erase(id); + } +} diff --git a/src/kernel/capabilities.h b/src/kernel/capabilities.h index dcf2d86..8cffc06 100644 --- a/src/kernel/capabilities.h +++ b/src/kernel/capabilities.h @@ -33,8 +33,16 @@ public: /// Create a new capability from an existing one. j6_handle_t derive(j6_handle_t id, j6_cap_t caps); - /// Get the capability referenced by a handle - capability * find(j6_handle_t id); + /// Get the capability referenced by a handle. + capability * retain(j6_handle_t id); + + /// Release a retained capability when it is no longer being used. + void release(j6_handle_t id); + + /// Get the capability referenced by a handle, without increasing + /// its refcount. WARNING: You probably want retain(), using a capability + /// (or the object it points to) without retaining it is dangerous. + capability * find_without_retain(j6_handle_t id); private: using map_type = util::inplace_map; diff --git a/src/kernel/objects/process.cpp b/src/kernel/objects/process.cpp index 724ef53..d022f3e 100644 --- a/src/kernel/objects/process.cpp +++ b/src/kernel/objects/process.cpp @@ -1,3 +1,5 @@ +#include + #include #include "assert.h" @@ -119,28 +121,44 @@ process::thread_exited(thread *th) void process::add_handle(j6_handle_t handle) { - m_handles.add(handle); + capability *c = g_cap_table.retain(handle); + kassert(c, "Trying to add a non-existant handle to a process!"); + + if (c) { + util::scoped_lock lock {m_handles_lock}; + m_handles.add(handle); + } } bool -process::remove_handle(j6_handle_t id) +process::remove_handle(j6_handle_t handle) { - return m_handles.remove(id); + util::scoped_lock lock {m_handles_lock}; + bool removed = m_handles.remove(handle); + lock.release(); + + if (removed) + g_cap_table.release(handle); + + return removed; } bool -process::has_handle(j6_handle_t id) +process::has_handle(j6_handle_t handle) { - return m_handles.contains(id); + util::scoped_lock lock {m_handles_lock}; + return m_handles.contains(handle); } size_t process::list_handles(j6_handle_descriptor *handles, size_t len) { + util::scoped_lock lock {m_handles_lock}; + size_t count = 0; for (j6_handle_t handle : m_handles) { - capability *cap = g_cap_table.find(handle); + capability *cap = g_cap_table.find_without_retain(handle); kassert(cap, "Found process handle that wasn't in the cap table"); if (!cap) continue; diff --git a/src/kernel/objects/process.h b/src/kernel/objects/process.h index 0986bf6..e2b2511 100644 --- a/src/kernel/objects/process.h +++ b/src/kernel/objects/process.h @@ -3,7 +3,6 @@ /// Definition of process kobject types #include -#include #include #include @@ -114,6 +113,7 @@ private: util::vector m_threads; util::node_set m_handles; + util::spinlock m_handles_lock; enum class state : uint8_t { running, exited }; state m_state; diff --git a/src/kernel/syscall_verify.cpp.cog b/src/kernel/syscall_verify.cpp.cog index 14cda18..bbd7170 100644 --- a/src/kernel/syscall_verify.cpp.cog +++ b/src/kernel/syscall_verify.cpp.cog @@ -184,10 +184,22 @@ for id, scope, method in syscalls.methods: cog.outl() if "noreturn" in method.options: + if handles: + cog.outl(f""" static_assert(false, "Cannot define a `noreturn` syscall that holds handles.");""") cog.outl(f""" {name}({", ".join(args)});""") + else: - cog.outl(f""" profiler profile {{"{name}"}};""") - cog.outl(f""" return {name}({", ".join(args)});""") + cog.outl(" j6_status_t _retval;") + cog.outl(" {") + cog.outl(f""" profiler profile {{"{name}"}};""") + cog.outl(f""" _retval = {name}({", ".join(args)});""") + cog.outl(" }\n") + + for type, arg, caps in handles: + cog.outl(f" release_handle({arg});") + + cog.outl(f""" return _retval;""") + cog.outl("}") cog.outl("\n") ]]]*/ diff --git a/src/kernel/syscalls/helpers.h b/src/kernel/syscalls/helpers.h index c560453..9dfb125 100644 --- a/src/kernel/syscalls/helpers.h +++ b/src/kernel/syscalls/helpers.h @@ -30,7 +30,7 @@ j6_status_t get_handle(j6_handle_t id, j6_cap_t caps, T *&object) return j6_status_ok; } - capability *capdata = g_cap_table.find(id); + capability *capdata = g_cap_table.retain(id); if (!capdata || capdata->type != T::type) return j6_err_invalid_arg; @@ -56,7 +56,7 @@ inline j6_status_t get_handle(j6_handle_t id, j6_cap_t caps, obj:: return j6_status_ok; } - capability *capdata = g_cap_table.find(id); + capability *capdata = g_cap_table.retain(id); if (!capdata) return j6_err_invalid_arg; @@ -68,4 +68,7 @@ inline j6_status_t get_handle(j6_handle_t id, j6_cap_t caps, obj:: return j6_status_ok; } +inline void release_handle(j6_handle_t id) { g_cap_table.release(id); } +inline void release_handle(j6_handle_t *id) { g_cap_table.release(*id); } + } // namespace syscalls