T276: Fix monitor revoke check to check for overlapping descendants instead of any...
authorSimon Gerber <simon.gerber@inf.ethz.ch>
Tue, 2 Aug 2016 11:56:55 +0000 (13:56 +0200)
committerSimon Gerber <simon.gerber@inf.ethz.ch>
Tue, 2 Aug 2016 11:56:55 +0000 (13:56 +0200)
Signed-off-by: Simon Gerber <simon.gerber@inf.ethz.ch>

13 files changed:
if/intermon.if
include/barrelfish_kpi/capabilities.h
kernel/arch/x86_64/syscall.c
kernel/include/syscall.h
kernel/monitor.c
usr/monitor/capops/capsend.c
usr/monitor/capops/init.c
usr/monitor/capops/internal.h
usr/monitor/capops/retype.c
usr/monitor/include/arch/x86_64/monitor_invocations_arch.h
usr/monitor/include/capsend.h
usr/monitor/include/monitor_invocations.h
usr/monitor/invocations.c

index 8dd6e6c..1b3180b 100644 (file)
@@ -169,6 +169,11 @@ interface intermon "The Interface between monitors" {
     message capops_find_descendants(caprep cap, capop_st st);
     message capops_find_descendants_result(errval status, capop_st st);
 
+    // Retypeability check
+    message capops_check_retypeable(caprep cap, capop_st st, uint64 offset,
+                                    uint64 objsize, uint64 count);
+    message capops_check_retypeable_result(errval status, capop_st st);
+
     /* Tracing Framework */
 
     // Notify a core that it should prepare the tracing state. The origin core
index 5f2dbdb..140ae7c 100644 (file)
@@ -416,6 +416,7 @@ enum kernel_cmd {
     KernelCmd_Clear_step,
     KernelCmd_Retype,
     KernelCmd_Has_descendants,
+    KernelCmd_Is_retypeable,
     KernelCmd_Sync_timer,
     KernelCmd_IPI_Register,
     KernelCmd_IPI_Delete,
index 1adfd42..d325568 100644 (file)
@@ -308,6 +308,19 @@ static struct sysret monitor_handle_has_descendants(struct capability *kernel_ca
     };
 }
 
+static struct sysret monitor_handle_is_retypeable(struct capability *kernel_cap,
+                                                  int cmd, uintptr_t *args)
+{
+    struct capability *src = (struct capability*)args;
+    int pos = ROUND_UP(sizeof(struct capability), sizeof(uint64_t)) / sizeof(uint64_t);
+
+    uintptr_t offset  = args[pos];
+    uintptr_t objsize = args[pos + 1];
+    uintptr_t count   = args[pos + 2];
+
+    return sys_monitor_is_retypeable(src, offset, objsize, count);
+}
+
 static struct sysret monitor_handle_delete_last(struct capability *kernel_cap,
                                                 int cmd, uintptr_t *args)
 {
@@ -1230,6 +1243,7 @@ static invocation_handler_t invocations[ObjType_Num][CAP_MAX_CMD] = {
         [KernelCmd_Unlock_cap]   = monitor_unlock_cap,
         [KernelCmd_Retype]       = monitor_handle_retype,
         [KernelCmd_Has_descendants] = monitor_handle_has_descendants,
+        [KernelCmd_Is_retypeable] = monitor_handle_is_retypeable,
         [KernelCmd_Delete_last]  = monitor_handle_delete_last,
         [KernelCmd_Delete_foreigns] = monitor_handle_delete_foreigns,
         [KernelCmd_Revoke_mark_target] = monitor_handle_revoke_mark_tgt,
index 6d69203..d82f647 100644 (file)
@@ -100,6 +100,12 @@ struct sysret sys_monitor_copy_existing(struct capability *src,
                                         cslot_t slot);
 
 /*
+ * Monitor syscall for retype
+ */
+struct sysret sys_monitor_is_retypeable(struct capability *source, gensize_t offset,
+                                        gensize_t objsize, size_t count);
+
+/*
  * Monitor syscalls for delete & revoke
  */
 
index 26e9994..d993dd7 100644 (file)
@@ -321,6 +321,59 @@ struct sysret sys_monitor_copy_existing(struct capability *src,
     return SYSRET(caps_copy_to_cnode(cnode, slot, copy, false, 0, 0));
 }
 
+/**
+ * \brief Check whether source has overlapping descendants
+ */
+struct sysret sys_monitor_is_retypeable(struct capability *source, gensize_t offset,
+                                        gensize_t objsize, size_t count)
+{
+    struct cte *next = mdb_find_greater(source, false);
+    if (!next || !is_ancestor(&next->cap, source)) {
+        // next does not exist or is not a descendant of source
+        return SYSRET(SYS_ERR_OK);
+    }
+
+    // Here: next is descendant of source; check for overlapping descendants
+    // XXX: this is copied from caps_retype()
+    errval_t err;
+    int find_range_result = 0;
+    struct cte *found_cte = NULL;
+    err = mdb_find_range(get_type_root(source->type),
+                         get_address(source) + offset,
+                         objsize * count,
+                         MDB_RANGE_FOUND_SURROUNDING,
+                         &found_cte,
+                         &find_range_result);
+    // this should never return an error unless we mess up the
+    // non-user supplied arguments
+    if (err_is_fail(err)) {
+        printk(LOG_WARN, "%s: mdb_find_range returned: %"PRIuERRV"\n", __FUNCTION__, err);
+        // XXX: error
+        return SYSRET(SYS_ERR_CAP_NOT_FOUND);
+    }
+    assert(err_is_ok(err));
+    // return REVOKE_FIRST, if we found a cap inside the region
+    // (FOUND_INNER == 2) or overlapping the region (FOUND_PARTIAL == 3)
+    if (find_range_result >= MDB_RANGE_FOUND_INNER) {
+        debug(SUBSYS_CAPS,
+                "%s: found existing region inside, or overlapping requested region:\n",
+                __FUNCTION__);
+        return SYSRET(SYS_ERR_REVOKE_FIRST);
+    }
+    // return REVOKE_FIRST, if we found a cap that isn't our source
+    // (or a copy of our source) covering the whole requested region.
+    else if (find_range_result == MDB_RANGE_FOUND_SURROUNDING &&
+            !is_copy(&found_cte->cap, source))
+    {
+        debug(SUBSYS_CAPS,
+                "%s: found non source region fully covering requested region\n",
+                __FUNCTION__);
+        return SYSRET(SYS_ERR_REVOKE_FIRST);
+    }
+
+    return SYSRET(SYS_ERR_OK);
+}
+
 struct sysret sys_monitor_delete_last(capaddr_t root_addr, uint8_t root_level,
                                       capaddr_t target_addr, uint8_t target_level,
                                       capaddr_t ret_cn_addr, uint8_t ret_cn_level,
index c225deb..8989a65 100644 (file)
@@ -559,6 +559,161 @@ find_descendants_result__rx_handler(struct intermon_binding *b, errval_t status,
 
 
 /*
+ * Check retypeability {{{1
+ */
+
+struct check_retypeable_mc_st {
+    struct capsend_mc_st mc_st;
+    capsend_result_fn result_fn;
+    void *st;
+    // msg args
+    gensize_t offset;
+    gensize_t objsize;
+    size_t count;
+    bool have_result;
+};
+
+static errval_t
+check_retypeable_send_cont(struct intermon_binding *b, intermon_caprep_t *caprep, struct capsend_mc_st *mc_st)
+{
+    DEBUG_CAPOPS("%s\n", __FUNCTION__);
+    lvaddr_t lst = (lvaddr_t)mc_st;
+    struct check_retypeable_mc_st *rst = (struct check_retypeable_mc_st *)mc_st;
+    return intermon_capops_check_retypeable__tx(b, NOP_CONT, *caprep,
+                (genvaddr_t)lst, rst->offset, rst->objsize, rst->count);
+}
+
+errval_t
+capsend_check_retypeable(struct domcapref src, gensize_t offset, gensize_t objsize,
+                         size_t count, capsend_result_fn result_fn, void *st)
+{
+    DEBUG_CAPOPS("%s\n", __FUNCTION__);
+    errval_t err;
+
+    struct capability cap;
+    err = monitor_domains_cap_identify(src.croot, src.cptr, src.level, &cap);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    struct check_retypeable_mc_st *mc_st;
+    mc_st = malloc(sizeof(*mc_st));
+    if (!mc_st) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+
+    // Setup multicast state
+    mc_st->result_fn   = result_fn;
+    mc_st->st          = st;
+    mc_st->offset      = offset;
+    mc_st->objsize     = objsize;
+    mc_st->count       = count;
+    mc_st->have_result = false;
+
+    DEBUG_CAPOPS("%s: broadcasting check_retypeable\n", __FUNCTION__);
+    return capsend_relations(&cap, check_retypeable_send_cont,
+            (struct capsend_mc_st*)mc_st, NULL);
+}
+
+
+struct check_retypeable_result_msg_st {
+    struct intermon_msg_queue_elem queue_elem;
+    errval_t status;
+    genvaddr_t st;
+};
+
+static void
+check_retypeable_result_send_cont(struct intermon_binding *b, struct intermon_msg_queue_elem *e)
+{
+    DEBUG_CAPOPS("%s\n", __FUNCTION__);
+    errval_t err;
+    struct check_retypeable_result_msg_st *msg_st;
+    msg_st = (struct check_retypeable_result_msg_st*)e;
+    err = intermon_capops_check_retypeable_result__tx(b, NOP_CONT, msg_st->status, msg_st->st);
+
+    if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
+        DEBUG_CAPOPS("%s: got FLOUNDER_ERR_TX_BUSY; requeueing msg.\n", __FUNCTION__);
+        struct intermon_state *inter_st = (struct intermon_state *)b->st;
+        // requeue send request at front and return
+        err = intermon_enqueue_send_at_front(b, &inter_st->queue, b->waitset,
+                                             (struct msg_queue_elem *)e);
+        GOTO_IF_ERR(err, handle_err);
+        return;
+    }
+
+handle_err:
+    free(msg_st);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "could not send check_retypeable_result");
+    }
+}
+
+void
+check_retypeable__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep,
+                             genvaddr_t st, gensize_t offset, gensize_t objsize,
+                             size_t count)
+{
+    DEBUG_CAPOPS("%s\n", __FUNCTION__);
+    errval_t err;
+
+    struct intermon_state *inter_st = (struct intermon_state*)b->st;
+    coreid_t from = inter_st->core_id;
+
+    struct capability cap;
+    caprep_to_capability(&caprep, &cap);
+
+    err = monitor_is_retypeable(&cap, offset, objsize, count);
+
+    DEBUG_CAPOPS("%s: got %s from kernel\n", __FUNCTION__, err_getcode(err));
+
+    struct check_retypeable_result_msg_st *msg_st;
+    msg_st = malloc(sizeof(*msg_st));
+    if (!msg_st) {
+        err = LIB_ERR_MALLOC_FAIL;
+        USER_PANIC_ERR(err, "could not alloc check_retypeable_result_msg_st");
+    }
+    msg_st->queue_elem.cont = check_retypeable_result_send_cont;
+    msg_st->st = st;
+    msg_st->status = err;
+
+    err = capsend_target(from, (struct msg_queue_elem*)msg_st);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "could not enqueue check_retypeable_result msg");
+    }
+}
+
+void
+check_retypeable_result__rx_handler(struct intermon_binding *b, errval_t status, genvaddr_t st)
+{
+    DEBUG_CAPOPS("%s: got %s from %d\n", __FUNCTION__, err_getcode(status),
+                 ((struct intermon_state *) b->st)->core_id);
+    lvaddr_t lst = (lvaddr_t) st;
+    struct check_retypeable_mc_st *mc_st = (struct check_retypeable_mc_st*)lst;
+
+    // Short-circuit when we get SYS_ERR_REVOKE_FIRST
+    if (err_no(status) == SYS_ERR_REVOKE_FIRST) {
+        if (!mc_st->have_result) {
+            DEBUG_CAPOPS("%s: short-circuit with status=%s\n", __FUNCTION__,
+                    err_getcode(status));
+            mc_st->have_result = true;
+            mc_st->result_fn(status, mc_st->st);
+        }
+    }
+
+    if (capsend_handle_mc_reply(&mc_st->mc_st)) {
+        // If we haven't called the callback yet, call it now with the last
+        // status value. Calling code needs to figure out what
+        // SYS_ERR_CAP_NOT_FOUND means.
+        if (!mc_st->have_result) {
+            DEBUG_CAPOPS("%s: notifying caller with final status=%s\n", __FUNCTION__,
+                    err_getcode(status));
+            mc_st->result_fn(status, mc_st->st);
+        }
+        free(mc_st);
+    }
+}
+
+/*
  * Ownership update {{{1
  */
 
index 4a00d6b..c47e941 100644 (file)
@@ -31,6 +31,8 @@ errval_t capops_init(struct waitset *ws, struct intermon_binding *b)
     b->rx_vtbl.capops_find_cap_result         = find_cap_result__rx_handler;
     b->rx_vtbl.capops_find_descendants        = find_descendants__rx_handler;
     b->rx_vtbl.capops_find_descendants_result = find_descendants_result__rx_handler;
+    b->rx_vtbl.capops_check_retypeable        = check_retypeable__rx_handler;
+    b->rx_vtbl.capops_check_retypeable_result = check_retypeable_result__rx_handler;
 
     delete_steps_init(ws);
 
index edf01fb..6f225b4 100644 (file)
@@ -72,6 +72,11 @@ void find_descendants__rx_handler(struct intermon_binding *b,
                                   intermon_caprep_t caprep, genvaddr_t st);
 void find_descendants_result__rx_handler(struct intermon_binding *b,
                                          errval_t status, genvaddr_t st);
+void check_retypeable__rx_handler(struct intermon_binding *b, intermon_caprep_t caprep,
+                                  genvaddr_t st, gensize_t offset, gensize_t objsize,
+                                  size_t count);
+void check_retypeable_result__rx_handler(struct intermon_binding *b,
+                                         errval_t status, genvaddr_t st);
 void owner_updated__rx_handler(struct intermon_binding *b, genvaddr_t st);
 void update_owner__rx_handler(struct intermon_binding *b,
                               intermon_caprep_t caprep, genvaddr_t st);
index c99f279..fbff529 100644 (file)
@@ -202,17 +202,15 @@ retype_request__rx(struct intermon_binding *b, intermon_caprep_t srcrep, uint64_
     }
 
     // check retypeability on self (owner)
-    bool has_descendants = false;
-    err = monitor_has_descendants(&cap, &has_descendants);
-    GOTO_IF_ERR(err, cont_err);
-    if (has_descendants) {
-        // If we have descendants, we cannot revoke; set err correctly and
-        // skip checking retypeability on other cores.
-        err = SYS_ERR_REVOKE_FIRST;
+    err = monitor_is_retypeable(&cap, req_st->check.offset,
+                                req_st->check.objsize, req_st->check.count);
+    // If this returns an error, including SYS_ERR_REVOKE_FIRST, we do not
+    // have to check retypeability on the other cores.
+    if (err_is_fail(err)) {
         goto cont_err;
     }
 
-    // initiate check
+    // initiate check on other cores
     check_retype__enq(&req_st->check);
 
     return;
@@ -303,19 +301,15 @@ err_cont:
  * \brief The descendants search has completed
  */
 static void
-find_descendants__rx(errval_t status, void *st)
+check_retypeable__rx(errval_t status, void *st)
 {
-    DEBUG_CAPOPS("%s\n", __FUNCTION__);
+    DEBUG_CAPOPS("%s: status=%s\n", __FUNCTION__, err_getcode(status));
     struct retype_check_st *check_st = (struct retype_check_st*)st;
 
     // need to translate error codes:
-    // - descendants found -> revoke first
     // - not found -> ok
     // - otherwise -> unchanged
-    if (err_is_ok(status)) {
-        status = SYS_ERR_REVOKE_FIRST;
-    }
-    else if (err_no(status) == SYS_ERR_CAP_NOT_FOUND) {
+    if (err_no(status) == SYS_ERR_CAP_NOT_FOUND) {
         status = err_push(status, SYS_ERR_OK);
     }
 
@@ -351,9 +345,10 @@ check_retype__enq(struct retype_check_st *check_st)
                            check_st->src.level);
     GOTO_IF_ERR(err, cont_err);
 
-    DEBUG_CAPOPS("%s: finding descendants of cap\n", __FUNCTION__);
-    err = capsend_find_descendants(check_st->src, find_descendants__rx,
-                                   check_st);
+    DEBUG_CAPOPS("%s: checking retypeability of cap\n", __FUNCTION__);
+    err = capsend_check_retypeable(check_st->src, check_st->offset,
+                                   check_st->objsize, check_st->count,
+                                   check_retypeable__rx, check_st);
     GOTO_IF_ERR(err, unlock_cap);
 
     return;
index ab514b7..f51709e 100644 (file)
@@ -94,4 +94,13 @@ invoke_monitor_has_descendants(uint64_t *raw, bool *res)
     return sysret.error;
 }
 
+static inline errval_t
+invoke_monitor_is_retypeable(uint64_t *raw, gensize_t offset,
+                             gensize_t objsize, size_t count)
+{
+    assert(sizeof(struct capability) <= 3*sizeof(uint64_t));
+    return cap_invoke7(cap_kernel, KernelCmd_Is_retypeable,
+                       raw[0], raw[1], raw[2], offset, objsize, count).error;
+}
+
 #endif
index 975b06d..6d6e304 100644 (file)
@@ -70,4 +70,8 @@ errval_t capsend_find_descendants(struct domcapref src,
                                   capsend_result_fn result_fn,
                                   void *st);
 
+errval_t capsend_check_retypeable(struct domcapref src, gensize_t offset,
+                                  gensize_t objsize, size_t count,
+                                  capsend_result_fn result_fn, void *st);
+
 #endif
index e494c2d..3f46d12 100644 (file)
@@ -237,6 +237,8 @@ errval_t monitor_set_cap_owner(struct capref croot, capaddr_t cptr, int level, c
 errval_t monitor_lock_cap(struct capref croot, capaddr_t cptr, int level);
 errval_t monitor_unlock_cap(struct capref croot, capaddr_t cptr, int level);
 errval_t monitor_has_descendants(struct capability *cap, bool *res);
+errval_t monitor_is_retypeable(struct capability *cap, gensize_t offset,
+                               gensize_t objsize, size_t count);
 
 static inline errval_t
 monitor_get_domcap_owner(struct domcapref cap, coreid_t *ret_owner)
index 76e897f..1e0ce2e 100644 (file)
@@ -229,6 +229,12 @@ errval_t monitor_has_descendants(struct capability *cap, bool *res)
     return invoke_monitor_has_descendants((uint64_t*)cap, res);
 }
 
+errval_t monitor_is_retypeable(struct capability *cap, gensize_t offset,
+                               gensize_t objsize, size_t count)
+{
+    return invoke_monitor_is_retypeable((uint64_t*)cap, offset, objsize, count);
+}
+
 errval_t monitor_delete_last(struct capref croot, capaddr_t cptr, int level, struct capref ret_cap)
 {
     capaddr_t root_addr = get_cap_addr(croot);