// vim: ft=cpp #include #include #include #include #include #include #include "profiler.h" #include "syscalls/helpers.h" /*[[[cog code generation from definitions.context import Context ctx = Context(definitions_path) ctx.parse("syscalls.def") syscalls = ctx.interfaces["syscalls"] for obj in syscalls.exposes: cog.outl(f'#include "objects/{obj.cname}.h"') ]]]*/ //[[[end]]] /*[[[cog code generation cog.outl(f'constexpr size_t syscall_count = {len(syscalls.methods)};') ]]]*/ //[[[end]]] DECLARE_PROFILE_CLASS(syscall_profiles, syscall_count); namespace { enum class req { required, optional, zero_ok }; template __attribute__((always_inline)) inline bool range_check(const T* param) { return reinterpret_cast(param) < arch::kernel_offset; } template __attribute__((always_inline)) inline bool check_refparam(const T* param, req required) { return range_check(param) && ((required == req::optional) || param); } template __attribute__((always_inline)) inline bool check_refparam(const T* param, size_t len, req required) { bool nullok = (required == req::optional) || (required == req::zero_ok && !len); return range_check(param) && (param || nullok); } template __attribute__((always_inline)) inline bool check_refparam(const T* param, size_t *len, req required) { if (!range_check(len)) return false; if (len) return check_refparam(param, *len, required); // if len is null but param is not, call that invalid. else return !param && required == req::optional; } // When called with more than 6 arguments, the 7th argument to the // verify function is the stack pointer holding the rest. "Pop" them // into variables to be passed normally with pop_from. template inline T pop_from(uint64_t *&sp) { return *reinterpret_cast(sp++); } } DEFINE_PROFILE_CLASS(syscall_profiles); namespace syscalls { using util::buffer; /*[[[cog code generation cbool = {True: "true", False: "false"} def get_caps(opts, type): caps = opts.get("cap", list()) return [f"j6_cap_{type.name}_{c}" for c in caps] for id, scope, method in syscalls.methods: if scope: name = f"{scope.name}_{method.name}" else: name = f"{method.name}" args = [] argdefs = [] cxxargdefs = [] refparams = [] handles = [] objparams = [] if method.constructor: argdefs.append(("j6_handle_t *", "self")) cxxargdefs.append("j6_handle_t *self") args.append("self") refparams.append(("self", "required")) elif not method.static: argdefs.append(("j6_handle_t", "self")) if "handle" in method.options: args.append("self_handle") cxxargdefs.append(f"obj::handle *self_handle") else: args.append("self_obj") cxxargdefs.append(f"obj::{scope.cname} *self") objparams.append((f"obj::{scope.cname} *", "self")) handles.append((scope.cname, "self", get_caps(method.options, scope))) for param in method.params: needs_obj = param.type.needs_object(param.options) needs_handle = ("handle" in param.options) or needs_obj for type, suffix in param.type.c_names(param.options): arg = f"{param.name}{suffix}" argdefs.append((type, arg)) for type, suffix in param.type.cxx_names(param.options): arg = f"{param.name}{suffix}" if needs_handle: handles.append((param.type.object.cname, arg, get_caps(param.options, param.type.object))) if needs_obj: objparams.append((type, arg)) args.append(f"{arg}_obj") cxxargdefs.append(f"{type} {arg}") else: args.append(f"{arg}_handle") cxxargdefs.append(f"obj::handle *{arg}_handle") break else: cxxargdefs.append(f"{type} {arg}") args.append(arg) if not needs_handle and param.caps: handles.append(( f"obj::{param.type.object.cname}", arg, get_caps(param.options, param.type.object))) if param.refparam: subs = param.type.c_names(param.options) checkargs = map(lambda x: f"{param.name}{x[1]}", subs) refparams.append((", ".join(checkargs), param.optional)) first_args = ", ".join(map(lambda x: f"{x[0]} {x[1]}", argdefs[:6])) extra_argdefs = argdefs[6:] if extra_argdefs: first_args += ", uint64_t *sp" attr = "" if "noreturn" in method.options: attr = "[[noreturn]] " cog.outl(f"""{attr}j6_status_t {name} ({", ".join(cxxargdefs)});""") cog.outl(f"""{attr}j6_status_t _syscall_verify_{name} ({first_args}) {{""") for type, arg in extra_argdefs: cog.outl(f" {type} {arg} = pop_from<{type}>(sp);") if extra_argdefs: cog.outl() for checkargs, optional in refparams: cog.outl(f" if (!check_refparam({checkargs}, req::{optional}))") cog.outl( " return j6_err_invalid_arg;") cog.outl() for type, arg, caps in handles: cog.outl(f" obj::handle *{arg}_handle = get_handle({arg});") cog.outl(f" if (!{arg}_handle) return j6_err_invalid_arg;") if caps: allcaps = " | ".join(caps) cog.outl(f" j6_cap_t {arg}_caps_req = {allcaps};") cog.outl(f" if (!{arg}_handle->has_cap({arg}_caps_req)) return j6_err_denied;") for type, arg in objparams: if type.endswith('*'): type = type[:-1].strip() cog.outl(f" {type} *{arg}_obj = {arg}_handle->as();") cog.outl(f" if (!{arg}_obj) return j6_err_invalid_arg;") cog.outl() if "noreturn" in method.options: cog.outl(f""" {name}({", ".join(args)});""") else: cog.outl(f""" profiler profile {{"{name}"}};""") cog.outl(f""" return {name}({", ".join(args)});""") cog.outl("}") cog.outl("\n") ]]]*/ //[[[end]]] } // namespace syscalls