/* * \brief Fork bomb to stress Genode * \author Christian Helmuth * \date 2007-08-16 * * The better part of this code is derived from the original init * implementation by Norman. */ /* * Copyright (C) 2007-2013 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Genode; class Bomb_child_resources { protected: Genode::Pd_connection _pd; Genode::Rom_connection _rom; Genode::Ram_connection _ram; Genode::Cpu_connection _cpu; char _name[32]; Bomb_child_resources(const char *file_name, const char *name, Genode::size_t ram_quota) : _pd(name), _rom(file_name, name), _ram(name), _cpu(name) { Genode::strncpy(_name, name, sizeof(_name)); _ram.ref_account(env()->ram_session_cap()); Genode::env()->ram_session()->transfer_quota(_ram.cap(), ram_quota); if (!_ram.cap().valid() || !_cpu.cap().valid()) { class Ram_or_cpu_session_not_valid { }; throw Ram_or_cpu_session_not_valid(); } } }; class Bomb_child : private Bomb_child_resources, public Genode::Child_policy, private Init::Child_policy_enforce_labeling, public Genode::List::Element { private: /* * Entry point used for serving the parent interface */ enum { STACK_SIZE = 2048 * sizeof(Genode::addr_t) }; Genode::Rpc_entrypoint _entrypoint; Genode::Child _child; Genode::Service_registry *_parent_services; Genode::Child_policy_dynamic_rom_file _config_policy; public: Bomb_child(const char *file_name, const char *unique_name, Genode::size_t ram_quota, Cap_session *cap_session, Service_registry *parent_services, unsigned generation) : Bomb_child_resources(file_name, unique_name, ram_quota), Init::Child_policy_enforce_labeling(Bomb_child_resources::_name), _entrypoint(cap_session, STACK_SIZE, "bomb_ep_child", false), _child(_rom.dataspace(), _pd.cap(), _ram.cap(), _cpu.cap(), &_entrypoint, this), _parent_services(parent_services), _config_policy("config", _entrypoint, &_ram) { char client_config[64]; snprintf(client_config, sizeof(client_config), "", generation); _config_policy.load(client_config, strlen(client_config) + 1); _entrypoint.activate(); } ~Bomb_child() { PLOG("%s", __PRETTY_FUNCTION__); } /**************************** ** Child-policy interface ** ****************************/ const char *name() const { return Bomb_child_resources::_name; } void filter_session_args(const char * x, char *args, Genode::size_t args_len) { Child_policy_enforce_labeling::filter_session_args(0, args, args_len); } Service *resolve_session_request(const char *service_name, const char *args) { Service * service = nullptr; /* check for config file request */ if ((service = _config_policy.resolve_session_request(service_name, args))) return service; return _parent_services->find(service_name); } }; /* * List of children * * Access to the children list from different threads * must be synchronized via the children lock. */ static Lock _children_lock; static List _children; /** * Check if a program with the specified name already exists */ static bool child_name_exists(const char *name) { Bomb_child *c = _children.first(); for ( ; c; c = c->List::Element::next()) if (strcmp(c->name(), name) == 0) return true; return false; } /** * Create a unique name based on the filename * * If a program with the filename as name already exists, we * add a counting number as suffix. */ static void get_unique_child_name(const char *filename, char *dst, size_t dst_len, unsigned generation) { Lock::Guard lock_guard(_children_lock); char buf[32]; char suffix[8]; suffix[0] = 0; for (int cnt = 1; true; cnt++) { /* build program name composed of filename and numeric suffix */ snprintf(buf, sizeof(buf), "%s_g%u%s", filename, generation, suffix); /* if such a program name does not exist yet, we are happy */ if (!child_name_exists(buf)) { strncpy(dst, buf, dst_len); return; } /* increase number of suffix */ snprintf(suffix, sizeof(suffix), ".%d", cnt + 1); } } /** * Start a child */ static int start_child(const char *file_name, Cap_session *cap_session, size_t ram_quota, Service_registry *parent_services, unsigned generation) { char name[64]; get_unique_child_name(file_name, name, sizeof(name), generation); Bomb_child *c = new (env()->heap()) Bomb_child(file_name, name, ram_quota, cap_session, parent_services, generation); Lock::Guard lock_guard(_children_lock); _children.insert(c); return 0; } /** * Kill child */ static void exit_child(Bomb_child *child) { destroy(env()->heap(), child); } /** * Request timer service * * \return timer session, or 0 if bomb is our parent */ Timer::Session *timer() { try { static Timer::Connection timer_inst; return &timer_inst; } catch (Parent::Service_denied) { } return 0; } int main(int argc, char **argv) { Genode::Xml_node node = config()->xml_node(); unsigned const rounds = node.attribute_value("rounds", 1U); unsigned const generation = node.attribute_value("generations", 1U); unsigned const children = node.attribute_value("children", 2U); unsigned const sleeptime = node.attribute_value("sleep", 2000U); unsigned long const demand = node.attribute_value("demand", 1024UL * 1024); printf("--- bomb started ---\n"); /* connect to core's cap service used for creating parent capabilities */ Cap_connection cap; /* names of services provided by the parent */ static const char *names[] = { "CAP", "RAM", "RM", "PD", "CPU", "ROM", "LOG", 0 }; static Service_registry parent_services; for (unsigned i = 0; names[i]; i++) parent_services.insert(new (env()->heap()) Parent_service(names[i])); unsigned long avail = env()->ram_session()->avail(); unsigned long amount = (avail - demand) / children; if (amount < (demand * children)) { PLOG("I'm a leaf node - generation %u - not enough memory.", generation); sleep_forever(); } if (generation == 0) { PLOG("I'm a leaf node - generation 0"); sleep_forever(); } for (unsigned round = 0; round < rounds ; ++round) { for (unsigned i = children; i; --i) start_child("bomb", &cap, amount, &parent_services, generation - 1); /* is init our parent? */ if (!timer()) sleep_forever(); /* don't ask parent for further resources if we ran out of memory */ static Signal_receiver sig_rec; static Signal_context sig_ctx_res_avail; if (round == 0) { /* prevent to block for resource upgrades caused by clients */ env()->parent()->resource_avail_sigh(sig_rec.manage(&sig_ctx_res_avail)); } timer()->msleep(sleeptime); PINF("[%03d] It's time to kill all my children...", round); while (1) { Bomb_child *c; _children_lock.lock(); c = _children.first(); if (c) _children.remove(c); _children_lock.unlock(); if (c) exit_child(c); else break; } PINF("[%03d] Done.", round); } /* master if we have a timer connection */ if (timer()) PINF("Done. Going to sleep"); sleep_forever(); return 0; }