[kernel] Add new zero_ok flag to syscall params

The new zero_ok flag is similar to optional for reference parameters,
but only in cases where there is a length parameter. If that parameter
is a reference parameter itself and is null, or it is non-null and
contains a non-zero length, or there is no length parameter, then the
main parameter may not be null.
This commit is contained in:
Justin C. Miller
2022-01-30 14:21:08 -08:00
parent b6218a1121
commit da5c1e9833
3 changed files with 37 additions and 13 deletions

View File

@@ -22,7 +22,7 @@ object endpoint : object {
# is available.
method receive [cap:receive] {
param tag uint64 [out]
param data buffer [out optional]
param data buffer [out zero_ok]
param timeout uint64 # Receive timeout in nanoseconds
}
@@ -31,7 +31,7 @@ object endpoint : object {
# operation.
method sendrecv [cap:send cap:receive] {
param tag uint64 [inout]
param data buffer [inout]
param data buffer [inout zero_ok]
param timeout uint64 # Receive timeout in nanoseconds
}
}

View File

@@ -62,5 +62,7 @@ class Param:
@property
def optional(self):
return "optional" in self.options
if "zero_ok" in self.options: return "zero_ok"
elif "optional" in self.options: return "optional"
else: return "required"

View File

@@ -22,11 +22,34 @@ for obj in syscalls.exposes:
//[[[end]]]
namespace {
enum class req { required, optional, zero_ok };
template <typename T>
__attribute__((always_inline))
inline bool check_refparam(const T* param, bool optional) {
return reinterpret_cast<uintptr_t>(param) < arch::kernel_offset &&
(optional || param);
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
@@ -67,7 +90,7 @@ for id, scope, method in syscalls.methods:
argdefs.append(("j6_handle_t *", "self"))
cxxargdefs.append("j6_handle_t *self")
args.append("self")
refparams.append(("self", False))
refparams.append(("self", "required"))
elif not method.static:
argdefs.append(("j6_handle_t", "self"))
@@ -104,10 +127,9 @@ for id, scope, method in syscalls.methods:
if param.refparam:
subs = param.type.c_names(param.options)
refparams.append((param.name + subs[0][1], param.optional))
if param.outparam:
for sub in subs[1:]:
refparams.append((param.name + sub[1], param.optional))
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:]
@@ -124,8 +146,8 @@ for id, scope, method in syscalls.methods:
if extra_argdefs:
cog.outl()
for pname, optional in refparams:
cog.outl(f" if (!check_refparam({pname}, {cbool[optional]}))")
for checkargs, optional in refparams:
cog.outl(f" if (!check_refparam({checkargs}, req::{optional}))")
cog.outl( " return j6_err_invalid_arg;")
cog.outl()