Files
jsix_import/src/kernel/syscall_verify.cpp.cog
Justin C. Miller 7b8fd76af0 [libj6] Move caps.h to cap_flags.h
This file is just the generated cap flag constants. Move it to not
collide with more capability code to be added.
2022-09-25 17:23:14 -07:00

205 lines
6.3 KiB
C++

// vim: ft=cpp
#include <stdint.h>
#include <arch/memory.h>
#include <j6/cap_flags.h>
#include <j6/errors.h>
#include <j6/types.h>
#include <util/counted.h>
#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 <typename T>
__attribute__((always_inline))
inline bool range_check(const T* param) {
return reinterpret_cast<uintptr_t>(param) < arch::kernel_offset;
}
template <typename T>
__attribute__((always_inline))
inline bool check_refparam(const T* param, req required) {
return range_check(param) && ((required == req::optional) || param);
}
template <typename T>
__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 <typename T>
__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 <typename T>
inline T pop_from(uint64_t *&sp) {
return *reinterpret_cast<T*>(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<typename obj::{type}>({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<typename {type}>();")
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<syscall_profiles, {id}> profile {{"{name}"}};""")
cog.outl(f""" return {name}({", ".join(args)});""")
cog.outl("}")
cog.outl("\n")
]]]*/
//[[[end]]]
} // namespace syscalls