From c6943d494b89fec2518bca1bde1bdbe56de3279c Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 5 Jun 2015 21:55:18 +0200 Subject: [PATCH] nova: extend platform test to provoke kernel panic Showcasing the out of memory kernel issue. One test triggers oom during memory delegation when talking to core pager thread. Two other test trigger oom during capability delegation in a server/client scenario for send and reply phase separately. Issue #1601 --- repos/base-nova/run/platform.run | 4 +- repos/base-nova/src/test/platform/main.cc | 140 ++++++++++++++++++++- repos/base-nova/src/test/platform/server.h | 81 ++++++++++++ 3 files changed, 222 insertions(+), 3 deletions(-) create mode 100644 repos/base-nova/src/test/platform/server.h diff --git a/repos/base-nova/run/platform.run b/repos/base-nova/run/platform.run index 638432ea7..5891bf005 100644 --- a/repos/base-nova/run/platform.run +++ b/repos/base-nova/run/platform.run @@ -18,7 +18,7 @@ install_config { - + } @@ -27,6 +27,6 @@ build_boot_image "core init test-platform" append qemu_args "-nographic -m 128" -run_genode_until {Test finished} 15 +run_genode_until {Test finished} 150 puts "\nTest succeeded" diff --git a/repos/base-nova/src/test/platform/main.cc b/repos/base-nova/src/test/platform/main.cc index 779032b0f..4057fb347 100644 --- a/repos/base-nova/src/test/platform/main.cc +++ b/repos/base-nova/src/test/platform/main.cc @@ -16,10 +16,121 @@ #include #include -using namespace Genode; +#include +#include + +#include "server.h" static unsigned failed = 0; +using namespace Genode; + +void test_server_oom() +{ + using namespace Genode; + + enum { STACK_SIZE = 4096 }; + + static Cap_connection cap; + static Rpc_entrypoint ep(&cap, STACK_SIZE, "rpc_ep"); + + Test::Component component; + Test::Capability session_cap = ep.manage(&component); + Test::Client client(session_cap); + + /* case that during reply we get oom */ + for (unsigned i = 0; i < 20000; i++) { + Genode::Native_capability got_cap = client.void_cap(); + + if (!got_cap.valid()) { + PERR("%u cap id %lx invalid", i, got_cap.local_name()); + failed ++; + break; + } + + /* be evil and keep this cap by manually incrementing the ref count */ + Cap_index idx(cap_map()->find(got_cap.local_name())); + idx.inc(); + + if (i % 5000 == 4999) + PINF("received %u. cap", i); + } + + /* case that during send we get oom */ + for (unsigned i = 0; i < 20000; i++) { + /* be evil and switch translation off - server ever uses a new selector */ + Genode::Native_capability send_cap = session_cap; + send_cap.solely_map(); + + if (!client.cap_void(send_cap)) { + PERR("sending %4u. cap failed", i); + failed ++; + break; + } + + if (i % 5000 == 4999) + PINF("sent %u. cap", i); + } + + ep.dissolve(&component); +} + +class Greedy : public Thread<4096> { + + public: + + Greedy() + : + Thread<0x1000>("greedy") + { } + + void entry() { + PINF("starting"); + + enum { SUB_RM_SIZE = 2UL * 1024 * 1024 * 1024 }; + + Genode::Rm_connection sub_rm(0, SUB_RM_SIZE); + addr_t const mem = env()->rm_session()->attach(sub_rm.dataspace()); + + Nova::Utcb * nova_utcb = reinterpret_cast(utcb()); + Nova::Rights const mapping_rwx(true, true, true); + + addr_t const page_fault_portal = tid().exc_pt_sel + 14; + + PERR("cause mappings in range [0x%lx, 0x%lx) %p", mem, + mem + SUB_RM_SIZE - 1, &mem); + + for (addr_t map_to = mem; map_to < mem + SUB_RM_SIZE; map_to += 4096) { + + /* setup faked page fault information */ + nova_utcb->items = ((addr_t)&nova_utcb->qual[2] - (addr_t)nova_utcb->msg) / sizeof(addr_t); + nova_utcb->ip = 0xbadaffe; + nova_utcb->qual[1] = (addr_t)&mem; + nova_utcb->crd_rcv = Nova::Mem_crd(map_to >> 12, 0, mapping_rwx); + + /* trigger faked page fault */ + Genode::uint8_t res = Nova::call(page_fault_portal); + if (res != Nova::NOVA_OK) { + PINF("call result=%u", res); + failed++; + return; + } + + /* check that we really got the mapping */ + touch_read(reinterpret_cast(map_to)); + + /* print status information in interval of 32M */ + if (!(map_to & (32UL * 1024 * 1024 - 1))) { + printf("0x%lx\n", map_to); + /* trigger some work to see quota in kernel decreasing */ +// Nova::Rights rwx(true, true, true); +// Nova::revoke(Nova::Mem_crd((map_to - 32 * 1024 * 1024) >> 12, 12, rwx)); + } + } + printf("still alive - done\n"); + } +}; + void check(uint8_t res, const char *format, ...) { static char buf[128]; @@ -73,6 +184,33 @@ int main(int argc, char **argv) check(res, "pt_ctrl %2u", i); } + /* upgrade available capability indices for this process */ + unsigned index = 512 * 1024; + static char local[128][sizeof(Cap_range)]; + + for (unsigned i = 0; i < sizeof(local) / sizeof (local[0]); i++) { + Cap_range * range = reinterpret_cast(local[i]); + *range = Cap_range(index); + + cap_map()->insert(range); + + index = range->base() + range->elements(); + }; + + /** + * Test to provoke out of memory during capability transfer of + * server/client. + * + * Set in hypervisor.ld the memory to a low value of about 1M to let + * trigger the test. + */ + test_server_oom(); + + /* Test to provoke out of memory in kernel during interaction with core */ + static Greedy core_pagefault_oom; + core_pagefault_oom.start(); + core_pagefault_oom.join(); + if (!failed) printf("Test finished\n"); diff --git a/repos/base-nova/src/test/platform/server.h b/repos/base-nova/src/test/platform/server.h new file mode 100644 index 000000000..00eead223 --- /dev/null +++ b/repos/base-nova/src/test/platform/server.h @@ -0,0 +1,81 @@ +/* + * \brief Dummy server interface + * \author Alexander Boettcher + */ + +/* + * Copyright (C) 2013-2015 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. + */ + +#pragma once + +/* Genode includes */ +#include +#include +#include +#include + +#include +#include + +namespace Test { struct Session; struct Client; struct Component; } + +/** + * Test session interface definition + */ +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_void_cap, Genode::Native_capability, + void_cap); + + GENODE_RPC_INTERFACE(Rpc_cap_void, Rpc_void_cap); +}; + +struct Test::Client : Genode::Rpc_client +{ + Client(Capability cap) : Rpc_client(cap) { } + + bool cap_void(Genode::Native_capability cap) { + return call(cap); } + + Genode::Native_capability void_cap() { + return call(); } +}; + +struct Test::Component : Genode::Rpc_object +{ + /* Test to transfer a object capability during send */ + bool cap_void(Genode::Native_capability); + /* Test to transfer a object capability during reply */ + Genode::Native_capability void_cap(); +}; + +namespace Test { typedef Genode::Capability Capability; } + +/** + * Session implementation + */ +bool Test::Component::cap_void(Genode::Native_capability got_cap) { + if (!got_cap.valid()) + return false; + + /* be evil and keep this cap by manually incrementing the ref count */ + Genode::Cap_index idx(Genode::cap_map()->find(got_cap.local_name())); + idx.inc(); + + return true; +} + +Genode::Native_capability Test::Component::void_cap() { + Genode::Native_capability send_cap = cap(); + /* be evil and switch translation off - client ever uses a new selector */ + send_cap.solely_map(); + return send_cap; +}