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
This commit is contained in:
Alexander Boettcher 2015-06-05 21:55:18 +02:00 committed by Christian Helmuth
parent 1feaf75605
commit c6943d494b
3 changed files with 222 additions and 3 deletions

View File

@ -18,7 +18,7 @@ install_config {
<any-service> <parent/> </any-service>
</default-route>
<start name="test-platform">
<resource name="RAM" quantum="1M"/>
<resource name="RAM" quantum="2M"/>
</start>
</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"

View File

@ -16,10 +16,121 @@
#include <base/printf.h>
#include <base/snprintf.h>
using namespace Genode;
#include <util/touch.h>
#include <rm_session/connection.h>
#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<Nova::Utcb *>(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<unsigned char *>(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<Cap_range *>(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");

View File

@ -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 <base/printf.h>
#include <base/thread.h>
#include <base/env.h>
#include <base/sleep.h>
#include <cap_session/connection.h>
#include <base/rpc_server.h>
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<Session>
{
Client(Capability<Session> cap) : Rpc_client<Session>(cap) { }
bool cap_void(Genode::Native_capability cap) {
return call<Rpc_cap_void>(cap); }
Genode::Native_capability void_cap() {
return call<Rpc_void_cap>(); }
};
struct Test::Component : Genode::Rpc_object<Test::Session, Test::Component>
{
/* 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<Test::Session> 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;
}