nova: support dropping caps just locally

without revocation of all subsequent delegations.

Fixes 
This commit is contained in:
Alexander Boettcher 2016-04-19 17:48:04 +02:00 committed by Christian Helmuth
parent 0ac1d1774d
commit 77c4510787
12 changed files with 373 additions and 47 deletions
repos/base-nova

View File

@ -60,7 +60,7 @@ namespace Genode {
Cap_range *find_by_id(addr_t);
void inc(unsigned id, bool inc_if_one = false);
void inc(unsigned id);
void dec(unsigned id, bool revoke = true, unsigned num_log2 = 0);
addr_t alloc(size_t const num_log2);
@ -88,10 +88,10 @@ namespace Genode {
bool valid() const { return _range; }
inline void inc(bool inc_if_one = false)
inline void inc()
{
if (_range)
_range->inc(_local_name - _range->base(), inc_if_one);
_range->inc(_local_name - _range->base());
}
inline void dec()

View File

@ -60,10 +60,10 @@ namespace Genode {
protected:
inline void _inc(bool inc_if_one = false) const
inline void _inc() const
{
Cap_index idx(cap_map()->find(local_name()));
idx.inc(inc_if_one);
idx.inc();
}
inline void _dec() const
@ -106,15 +106,6 @@ namespace Genode {
bool operator==(const Native_capability &o) const {
return local_name() == o.local_name(); }
/**
* Inhibit removal of capability from cap map if it's the last reference
*/
void keep_if_last_reference()
{
if (valid())
_inc(true);
}
/**
* Copy constructor
*/

View File

@ -298,21 +298,37 @@ namespace Nova {
* \param sm SM selector which gets an up() by the kernel if the
* memory of the current revoke invocation gets freed up
* (end of RCU period)
* \param kim keep_in_mdb - if set to true the kernel will make the
* resource inaccessible for solely for the specified pd.
* All already beforehand delegated resources will not be
* changed, e.g. revoked. All rights of the local resource
* will be removed (independent of what is specified by crd).
*/
ALWAYS_INLINE
inline uint8_t revoke(Crd crd, bool self = true, bool remote = false,
mword_t pd = 0, mword_t sm = 0)
mword_t pd = 0, mword_t sm = 0, bool kim = false)
{
uint8_t flags = self ? 0x1 : 0;
if (remote)
flags |= 0x2;
if (kim)
flags |= 0x4;
mword_t value_crd = crd.value();
return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd);
}
/*
* Shortcut for revoke, where solely the local cap should be revoked and
* not all subsequent delegations of the local cap.
*/
ALWAYS_INLINE
inline uint8_t drop(Crd crd) {
return revoke(crd, true, false, 0, 0, true); }
ALWAYS_INLINE
inline uint8_t lookup(Crd &crd)
{

View File

@ -250,21 +250,38 @@ namespace Nova {
* \param sm SM selector which gets an up() by the kernel if the
* memory of the current revoke invocation gets freed up
* (end of RCU period)
* \param kim keep_in_mdb - if set to true the kernel will make the
* resource inaccessible solely inside the specified pd.
* All already beforehand delegated resources will not be
* changed, e.g. revoked. All rights of the local resource
* will be removed (independent of what is specified by crd).
*/
ALWAYS_INLINE
inline uint8_t revoke(Crd crd, bool self = true, bool remote = false,
mword_t pd = 0, mword_t sm = 0)
mword_t pd = 0, mword_t sm = 0, bool kim = false)
{
uint8_t flags = self ? 0x1 : 0;
if (remote)
flags |= 0x2;
if (kim)
flags |= 0x4;
mword_t value_crd = crd.value();
return syscall_5(NOVA_REVOKE, flags, sm, value_crd, pd);
}
/*
* Shortcut for revoke, where solely the local cap should be revoked and
* not all subsequent delegations of the local cap.
*/
ALWAYS_INLINE
inline uint8_t drop(Crd crd) {
return revoke(crd, true, false, 0, 0, true); }
ALWAYS_INLINE
inline uint8_t lookup(Crd &crd)
{

View File

@ -1 +1 @@
061565ecaea829f5a5ab48e39b4fae9148da7e4b
bde8909f6367ea2767c54b85ddf90afc2e6baee8

View File

@ -4,7 +4,7 @@ DOWNLOADS := nova.git
# r9 branch - use r9_debug for more verbose kernel messages
URL(nova) := https://github.com/alex-ab/NOVA.git
REV(nova) := 172fe0dc1b6228fec20e220d2524f25b1016f6ab
REV(nova) := 8548c34df01504789b01f4e1a31b71e6d08a79c7
DIR(nova) := src/kernel/nova
PATCHES := $(wildcard $(REP_DIR)/patches/*.patch)

View File

@ -42,15 +42,12 @@ Cap_range *Cap_range::find_by_id(addr_t id)
}
void Cap_range::inc(unsigned id, bool inc_if_one)
void Cap_range::inc(unsigned id)
{
bool failure = false;
{
Lock::Guard guard(_lock);
if (inc_if_one && _cap_array[id] != 1)
return;
if (_cap_array[id] + 1 == 0)
failure = true;
else
@ -78,7 +75,7 @@ void Cap_range::dec(unsigned const id_start, bool revoke, unsigned num_log_2)
}
if (revoke && _cap_array[id] == 1)
Nova::revoke(Nova::Obj_crd(_base + id, 0));
Nova::drop(Nova::Obj_crd(_base + id, 0));
_cap_array[id]--;
}

View File

@ -166,15 +166,6 @@ void Rpc_entrypoint::_activation_entry()
return;
}
/*
* Inhibit removal of capabilities sent as results of client requests.
* This prevents the recursive revocation of NOVA portal caps and,
* therefore, permits clients to use result capabilities after server
* code dropped all references.
*/
for (unsigned i = 0; i < ep._snd_buf.used_caps(); ++i)
ep._snd_buf.cap(i).keep_if_last_reference();
/* dispatch request */
ep._snd_buf.reset();
try { exc = obj->dispatch(opcode, unmarshaller, ep._snd_buf); }

View File

@ -0,0 +1,59 @@
/*
* \brief Helper classes to make raw Nova IPC calls which can't be expressed
* via the Genode base RPC abstractions
* \author Alexander Boettcher
*
*/
/*
* Copyright (C) 2016 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#include <base/thread.h>
/* Genode includes */
#include <base/thread.h>
#include <base/printf.h>
/* test specific includes */
#include "server.h"
using namespace Test;
long Test::cap_void_manual(Genode::Native_capability dst,
Genode::Native_capability arg1,
Genode::addr_t &local_reply)
{
Genode::Thread * myself = Genode::Thread::myself();
Nova::Utcb *utcb = reinterpret_cast<Nova::Utcb *>(myself->utcb());
/* save original receive window */
Nova::Crd orig_crd = utcb->crd_rcv;
/* don't open receive window */
utcb->crd_rcv = Nova::Obj_crd();
/* not used on base-nova */
utcb->msg[0] = 0;
/* method number of RPC interface to be called on server side */
utcb->msg[1] = 0;
utcb->set_msg_word(2);
if (!arg1.valid())
return Genode::Rpc_exception_code::INVALID_OBJECT;
Nova::Obj_crd crd(arg1.local_name(), 0, arg1.dst().rights());
if (!utcb->append_item(crd, 0, false, false, false))
return Genode::Rpc_exception_code::INVALID_OBJECT;
Genode::uint8_t res = Nova::call(dst.local_name());
/* restore original receive window */
utcb->crd_rcv = orig_crd;
local_reply = utcb->msg[2];
return (res == Nova::NOVA_OK && utcb->msg_words() == 3)
? utcb->msg[0] : Genode::Rpc_exception_code::INVALID_OBJECT;
}

View File

@ -32,8 +32,236 @@ static unsigned failed = 0;
static unsigned check_pat = 1;
static Genode::Cap_connection cap;
using namespace Genode;
void test_translate()
{
enum { STACK_SIZE = 4096 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_translate");
Test::Component component;
Test::Capability session_cap = ep.manage(&component);
Test::Client client(session_cap);
Genode::addr_t local_name = Native_thread::INVALID_INDEX;
long rpc = Test::cap_void_manual(session_cap, session_cap, local_name);
if (rpc != Genode::Rpc_exception_code::SUCCESS ||
local_name == session_cap.local_name() ||
local_name == Native_thread::INVALID_INDEX)
{
failed ++;
PERR("%s: ipc call failed %lx", __func__, rpc);
ep.dissolve(&component);
return;
}
Genode::Native_capability copy1(local_name);
rpc = Test::cap_void_manual(session_cap, copy1, local_name);
if (rpc != Genode::Rpc_exception_code::SUCCESS ||
local_name == copy1.local_name() ||
local_name == Native_thread::INVALID_INDEX)
{
failed ++;
PERR("%s: ipc call failed %lx", __func__, rpc);
ep.dissolve(&component);
return;
}
Genode::Native_capability copy2(local_name);
PINF("delegation session_cap->copy1->copy2 0x%lx->0x%lx->0x%lx",
session_cap.local_name(), copy1.local_name(), copy2.local_name());
/* sanity checks translate which must work */
Genode::Native_capability got_cap = client.cap_cap(copy2.local_name());
if (got_cap.local_name() != copy1.local_name()) {
failed ++;
PERR("%u:%s translate failed", __LINE__, __func__);
ep.dissolve(&component);
return;
}
got_cap = client.cap_cap(copy1.local_name());
if (got_cap.local_name() != session_cap.local_name()) {
failed ++;
PERR("%u:%s translate failed", __LINE__, __func__);
ep.dissolve(&component);
return;
}
got_cap = client.cap_cap(session_cap.local_name());
if (got_cap.local_name() != session_cap.local_name()) {
failed ++;
PERR("%u:%s translate failed", __LINE__, __func__);
ep.dissolve(&component);
return;
}
/**
* Test special revoke by make the intermediate cap (copy1) inaccessible
* and check that translate of copy2 get the right results.
*/
Nova::Obj_crd crd_ses(copy1.local_name(), 0);
enum { SELF = true, LOCAL_REVOKE = false, LOCAL_PD = 0, NO_BLOCKING = 0, KEEP_IN_MDB = true };
Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING, KEEP_IN_MDB);
crd_ses = Nova::Obj_crd(copy1.local_name(), 0);
Genode::uint8_t res = Nova::lookup(crd_ses);
if (res != Nova::NOVA_OK || !crd_ses.is_null()) {
failed ++;
PERR("%u - lookup call failed err=%x", __LINE__, res);
ep.dissolve(&component);
return;
}
/* copy1 should be skipped and session_cap is the valid response */
got_cap = client.cap_cap(copy2.local_name());
if (got_cap.local_name() != session_cap.local_name()) {
failed ++;
PERR("%u:%s translate failed", __LINE__, __func__);
ep.dissolve(&component);
return;
}
ep.dissolve(&component);
}
void test_revoke()
{
enum { STACK_SIZE = 4096 };
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_revoke");
Test::Component component;
Test::Capability session_cap = ep.manage(&component);
Test::Client client(session_cap);
Genode::addr_t local_name = Native_thread::INVALID_INDEX;
long rpc = Test::cap_void_manual(session_cap, session_cap, local_name);
if (rpc != Genode::Rpc_exception_code::SUCCESS ||
local_name == session_cap.local_name() ||
local_name == Native_thread::INVALID_INDEX)
{
failed ++;
PERR("test_revoke ipc call failed %lx", rpc);
ep.dissolve(&component);
return;
}
Genode::Native_capability copy_session_cap(local_name);
rpc = Test::cap_void_manual(copy_session_cap, copy_session_cap, local_name);
if (rpc != Genode::Rpc_exception_code::SUCCESS ||
local_name == copy_session_cap.local_name() ||
local_name == Native_thread::INVALID_INDEX)
{
failed ++;
PERR("test_revoke ipc call failed %lx", rpc);
ep.dissolve(&component);
return;
}
Nova::Obj_crd crd_dst(local_name, 0);
Genode::uint8_t res = Nova::lookup(crd_dst);
if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 ||
crd_dst.order() != 0) {
failed ++;
PERR("%u - lookup call failed %x", __LINE__, res);
ep.dissolve(&component);
return;
}
Nova::Obj_crd crd_ses(copy_session_cap.local_name(), 0);
res = Nova::lookup(crd_ses);
if (res != Nova::NOVA_OK || crd_ses.base() != copy_session_cap.local_name() || crd_ses.type() != 3 ||
crd_ses.order() != 0) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_ses.is_null());
ep.dissolve(&component);
return;
}
res = Nova::lookup(crd_dst);
if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 ||
crd_dst.order() != 0) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null());
ep.dissolve(&component);
return;
}
crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0);
enum { SELF = true, LOCAL_REVOKE = false, LOCAL_PD = 0, NO_BLOCKING = 0, KEEP_IN_MDB = true };
Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING, KEEP_IN_MDB);
crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0);
res = Nova::lookup(crd_ses);
if (res != Nova::NOVA_OK || !crd_ses.is_null()) {
failed ++;
PERR("%u - lookup call failed err=%x", __LINE__, res);
ep.dissolve(&component);
return;
}
res = Nova::lookup(crd_dst);
if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 ||
crd_dst.order() != 0) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null());
ep.dissolve(&component);
return;
}
/*
* Request some other capability and place it on very same selector
* as used before by copy_session_cap
*/
Genode::Thread * myself = Genode::Thread::myself();
Genode::Native_capability pager_cap(myself->native_thread().ec_sel + 1);
request_event_portal(pager_cap, copy_session_cap.local_name(), 0, 0);
/* check whether the requested cap before is valid and placed well */
crd_ses = Nova::Obj_crd(copy_session_cap.local_name(), 0);
res = Nova::lookup(crd_ses);
if (res != Nova::NOVA_OK || crd_ses.base() != copy_session_cap.local_name() ||
crd_ses.type() != 3 || crd_ses.order() != 0) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_ses.is_null());
ep.dissolve(&component);
return;
}
/* revoke it */
Nova::revoke(crd_ses, SELF, LOCAL_REVOKE, LOCAL_PD, NO_BLOCKING);
/* the delegated cap to the client should still be there */
res = Nova::lookup(crd_dst);
if (res != Nova::NOVA_OK || crd_dst.base() != local_name || crd_dst.type() != 3 ||
crd_dst.order() != 0) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null());
ep.dissolve(&component);
return;
}
/* kill the original session capability */
ep.dissolve(&component);
/* manually: cap.free_rpc_cap(session_cap); */
/* the delegated cap to the client should be now invalid */
res = Nova::lookup(crd_dst);
if (res != Nova::NOVA_OK || !crd_dst.is_null()) {
failed ++;
PERR("%u - lookup call failed err=%x is_null=%u", __LINE__, res, crd_dst.is_null());
return;
}
}
void test_pat()
{
/* read out the tsc frequenzy once */
@ -47,8 +275,7 @@ void test_pat()
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep");
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_pat");
Test::Component component;
Test::Capability session_cap = ep.manage(&component);
@ -138,8 +365,7 @@ void test_server_oom()
enum { STACK_SIZE = 4096 };
static Cap_connection cap;
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep");
static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep_oom");
Test::Component component;
Test::Capability session_cap = ep.manage(&component);
@ -317,6 +543,12 @@ int main(int argc, char **argv)
/* test PAT kernel feature */
test_pat();
/* test special revoke */
test_revoke();
/* test translate together with special revoke */
test_translate();
/**
* Test to provoke out of memory during capability transfer of
* server/client.

View File

@ -21,7 +21,15 @@
#include <cap_session/connection.h>
#include <base/rpc_server.h>
namespace Test { struct Session; struct Client; struct Component; }
namespace Test {
struct Session;
struct Client;
struct Component;
long cap_void_manual(Genode::Native_capability dst,
Genode::Native_capability arg1,
Genode::addr_t &local_name);
}
/**
* Test session interface definition
@ -30,25 +38,31 @@ struct Test::Session : Genode::Session
{
static const char *service_name() { return "TEST"; }
GENODE_RPC(Rpc_cap_void, bool, cap_void,
Genode::Native_capability);
GENODE_RPC(Rpc_cap_void, bool, cap_void, Genode::Native_capability,
Genode::addr_t &);
GENODE_RPC(Rpc_void_cap, Genode::Native_capability,
void_cap);
GENODE_RPC(Rpc_cap_cap, Genode::Native_capability, cap_cap,
Genode::addr_t);
GENODE_RPC(Rpc_leak_utcb_address, Genode::addr_t, leak_utcb_address);
GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap, Rpc_leak_utcb_address);
GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap, Rpc_cap_cap,
Rpc_leak_utcb_address);
};
struct Test::Client : Genode::Rpc_client<Session>
{
Client(Capability<Session> cap) : Rpc_client<Session>(cap) { }
bool cap_void(Genode::Native_capability cap) {
return call<Rpc_cap_void>(cap); }
bool cap_void(Genode::Native_capability cap, Genode::addr_t &local_name) {
return call<Rpc_cap_void>(cap, local_name); }
Genode::Native_capability void_cap() {
return call<Rpc_void_cap>(); }
Genode::Native_capability cap_cap(Genode::addr_t cap) {
return call<Rpc_cap_cap>(cap); }
Genode::addr_t leak_utcb_address() {
return call<Rpc_leak_utcb_address>(); }
};
@ -56,9 +70,11 @@ struct Test::Client : Genode::Rpc_client<Session>
struct Test::Component : Genode::Rpc_object<Test::Session, Test::Component>
{
/* Test to transfer a object capability during send */
bool cap_void(Genode::Native_capability);
bool cap_void(Genode::Native_capability, Genode::addr_t &);
/* Test to transfer a object capability during reply */
Genode::Native_capability void_cap();
/* Test to transfer a specific object capability during reply */
Genode::Native_capability cap_cap(Genode::addr_t);
/* Leak utcb address of entrypoint to manipulate utcb receive window */
Genode::addr_t leak_utcb_address();
};
@ -68,7 +84,11 @@ namespace Test { typedef Genode::Capability<Test::Session> Capability; }
/**
* Session implementation
*/
bool Test::Component::cap_void(Genode::Native_capability got_cap) {
inline bool Test::Component::cap_void(Genode::Native_capability got_cap,
Genode::addr_t &local_name)
{
local_name = got_cap.local_name();
if (!got_cap.valid())
return false;
@ -79,7 +99,7 @@ bool Test::Component::cap_void(Genode::Native_capability got_cap) {
return true;
}
Genode::Native_capability Test::Component::void_cap() {
inline Genode::Native_capability Test::Component::void_cap() {
Genode::Native_capability send_cap = cap();
/* XXX this code does does no longer work since the removal of 'solely_map' */
@ -91,5 +111,8 @@ Genode::Native_capability Test::Component::void_cap() {
return send_cap;
}
Genode::addr_t Test::Component::leak_utcb_address() {
inline Genode::addr_t Test::Component::leak_utcb_address() {
return reinterpret_cast<Genode::addr_t>(Genode::Thread::myself()->utcb()); }
inline Genode::Native_capability Test::Component::cap_cap(Genode::addr_t cap) {
return Genode::Native_capability(cap); }

View File

@ -1,3 +1,3 @@
TARGET = test-platform
SRC_CC = main.cc
SRC_CC = main.cc ipc.cc
LIBS = base config