mirror of
https://github.com/genodelabs/genode.git
synced 2025-04-09 04:15:52 +00:00
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:
parent
1feaf75605
commit
c6943d494b
@ -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"
|
||||
|
@ -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");
|
||||
|
||||
|
81
repos/base-nova/src/test/platform/server.h
Normal file
81
repos/base-nova/src/test/platform/server.h
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user