|
- #include "guard.h"
- #include <assert.h>
- #include <string.h>
- #include <stdlib.h>
- #include <inttypes.h>
-
- static unsigned checks;
- #define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL line %d: %s\n", __LINE__, #x); exit(1); } checks++; } while (0)
- static const char *uuid = "11111111-1111-4111-8111-111111111111";
-
- static config_t allowed(void)
- {
- config_t c; defaults(&c);
- c.nzones = 1; strcpy(c.zones[0].uuid, uuid);
- strcpy(c.zones[0].alias, "fixture-web"); c.zones[0].policy = ALLOW;
- return c;
- }
- static process_t process(int pid)
- {
- process_t p; memset(&p, 0, sizeof (p));
- p.pid = pid; p.ppid = 100; p.zid = 9; p.initpid = 99; p.nlwp = 1;
- p.zone_generation = 20; p.start_sec = 1000; p.start_nsec = 123;
- p.rss_kib = 1048576; strcpy(p.uuid, uuid); strcpy(p.name, "php-fpm");
- return p;
- }
- static sample_t sample(double at, uint64_t free_pages, uint64_t scanner)
- {
- sample_t s; memset(&s, 0, sizeof (s)); s.at = at; s.free = free_pages;
- s.lots = 130875; s.des = 65437; s.min = 49077; s.scan = scanner;
- return s;
- }
- static void step(engine_t *e, config_t *c, double at, uint64_t free_pages, uint64_t scanner)
- {
- sample_t s = sample(at, free_pages, scanner); delta_t d = {0, 0, 0};
- engine_step(e, c, &s, d, d, d);
- }
- static int parse(const char *text, config_t *c)
- {
- FILE *f = tmpfile(); int result; char error[128];
- CHECK(f != NULL); CHECK(fputs(text, f) >= 0); rewind(f);
- result = config_read(f, c, error, sizeof (error)); fclose(f); return result;
- }
- static void configs(void)
- {
- config_t c;
- CHECK(parse("# defaults\n", &c) == 0 && !c.armed);
- CHECK(parse("ACTION_MODE dry-run\nprotect_process postgres\n", &c) == 0);
- CHECK(parse("ACTION_MODE armd\n", &c) != 0);
- CHECK(parse("ACTION_MODE armed\n", &c) != 0);
- CHECK(parse("ACTION_MODE dry-run\nACTION_MODE armed\n", &c) != 0);
- CHECK(parse("LOG_KEEP 0\n", &c) != 0);
- CHECK(parse("TERM_GRACE_SECONDS -1\n", &c) != 0);
- CHECK(parse("TERM_GRACE_SECONDS 1 garbage\n", &c) != 0);
- CHECK(parse("UNKNOWN foo\n", &c) != 0);
- CHECK(parse("PRE_SECONDS 20\nPRESSURE_SECONDS 10\n", &c) != 0);
- CHECK(parse("zone global global allow\n", &c) != 0);
- CHECK(parse("zone 11111111-1111-4111-8111-111111111111 web allow\nACTION_MODE armed\n", &c) == 0 && c.armed);
- CHECK(parse("zone 11111111-1111-4111-8111-111111111111 web allow\nzone 11111111-1111-4111-8111-111111111111 db protect\n", &c) != 0);
- CHECK(!uuid_valid("11111111-1111-4111-8111-11111111111g"));
- CHECK(!uuid_valid("00000000-0000-0000-0000-000000000000"));
- puts("PASS strict configuration, safe defaults, explicit UUID authorization");
- }
- static void protections(void)
- {
- config_t c = allowed(); process_t p = process(123), q;
- CHECK(!protected_process(&c, &p));
- q = p; q.zid = 0; CHECK(protected_process(&c, &q));
- q = p; q.pid = 1; CHECK(protected_process(&c, &q));
- q = p; q.pid = q.initpid; CHECK(protected_process(&c, &q));
- q = p; q.initpid = 0; CHECK(protected_process(&c, &q));
- q = p; q.zone_generation = 0; CHECK(protected_process(&c, &q));
- q = p; q.nlwp = 0; CHECK(protected_process(&c, &q));
- q = p; q.ppid = 0; CHECK(protected_process(&c, &q));
- q = p; strcpy(q.name, "sshd"); CHECK(protected_process(&c, &q));
- q = p; q.rss_kib = 100; CHECK(protected_process(&c, &q));
- q = p; q.uuid[0] = '2'; CHECK(protected_process(&c, &q));
- c.zones[0].policy = PROTECT; CHECK(protected_process(&c, &p));
- c.zones[0].policy = OBSERVE; CHECK(protected_process(&c, &p));
- c.zones[0].policy = ALLOW;
- strcpy(c.protected_names[c.nnames++], "php-fpm"); CHECK(protected_process(&c, &p));
- c.nnames = 0; c.protected_pids[c.npids++] = p.pid; CHECK(protected_process(&c, &p));
- q = p; CHECK(same_process(&p, &q));
- q.start_nsec++; CHECK(!same_process(&p, &q));
- q = p; q.zone_generation++; CHECK(!same_process(&p, &q));
- q = p; q.uuid[0] = '2'; CHECK(!same_process(&p, &q));
- puts("PASS mandatory protections, PID reuse and zone incarnation identity");
- }
- static void histories(void)
- {
- history_t h = {0}; delta_t d; unsigned i;
- for (i = 0; i <= 70; i++) history_add(&h, i, 10000 - 10 * i);
- d = history_delta(&h, 70, 5); CHECK(d.valid && d.value == -50 && d.age == 5);
- d = history_delta(&h, 70, 15); CHECK(d.valid && d.value == -150);
- d = history_delta(&h, 70, 60); CHECK(d.valid && d.value == -600);
- CHECK(!history_delta(&h, 90, 5).valid);
- memset(&h, 0, sizeof (h));
- history_add(&h, 0, 123); history_add(&h, 15, 100);
- CHECK(!history_delta(&h, 15, 5).valid);
- CHECK(history_delta(&h, 15, 15).value == -23);
- d.valid = 1; d.value = 100;
- CHECK(rank_score(1000, d) == 1200);
- CHECK(rank_score(UINT64_MAX - 10, d) == UINT64_MAX);
- d.value = -100; CHECK(rank_score(1000, d) == 1000);
- {
- uint64_t bytes = 0;
- CHECK(swap_physical_bytes_from_blocks(67024888, 58931824, &bytes) == 0);
- CHECK(bytes == UINT64_C(4143648768));
- CHECK(swap_physical_bytes_from_blocks(10, 11, &bytes) != 0);
- CHECK(swap_physical_bytes_from_blocks(UINT64_MAX, 0, &bytes) != 0);
- }
- puts("PASS timestamped 5/15/60s windows, missing data, signed growth, overflow");
- }
- static void states(void)
- {
- config_t c = allowed(); engine_t e; unsigned i;
- engine_init(&e);
- for (i = 0; i < 10; i++) step(&e, &c, i, 425950, 0);
- CHECK(e.state == NORMAL && !e.danger);
- for (i = 10; i <= 12; i++) step(&e, &c, i, 100000, 1);
- CHECK(e.state == NORMAL);
- step(&e, &c, 13, 100000, 1); CHECK(e.state == PRE_PRESSURE);
- for (i = 14; i <= 20; i++) step(&e, &c, i, 100000, 1);
- CHECK(e.state == PRESSURE && !e.danger);
- step(&e, &c, 21, 60000, 1); step(&e, &c, 22, 59000, 1);
- CHECK(!e.danger);
- step(&e, &c, 23, 58000, 1); CHECK(e.state == CRITICAL && e.danger);
- step(&e, &c, 24, 140000, 0); CHECK(e.state == RECOVERY && !e.danger);
- for (i = 25; i <= 40; i++) step(&e, &c, i, 140000, 0);
- CHECK(e.state == RECOVERY); /* below 10% margin */
- for (i = 41; i <= 56; i++) step(&e, &c, i, 150000, 0);
- CHECK(e.state == NORMAL);
- step(&e, &c, 57, 40000, 0); CHECK(e.state == EMERGENCY && e.danger && !e.floor);
- step(&e, &c, 58, 20000, 0); CHECK(e.floor);
- engine_init(&e);
- for (i = 0; i < 20; i++) step(&e, &c, i, 60000, 0);
- CHECK(!e.danger); /* low but stable, no scanner */
- engine_init(&e);
- step(&e, &c, 0, 60000, 1); step(&e, &c, 20, 59000, 1);
- CHECK(!e.danger); /* gaps do not count as sustained samples */
- engine_gap(&e); step(&e, &c, 21, 58000, 1); CHECK(!e.danger);
- step(&e, &c, 22, 57000, 1); step(&e, &c, 23, 56000, 1); CHECK(e.danger);
- {
- sample_t s = sample(1, 60000, 0);
- delta_t none = {0, 0, 0}, rising = {1, 5, 4096};
- engine_init(&e); engine_step(&e, &c, &s, none, rising, none);
- CHECK(!e.danger && e.critical_since < 0); /* physical swap alone is inert */
- engine_init(&e); engine_step(&e, &c, &s, none, none, rising);
- CHECK(!e.danger && e.critical_since < 0); /* pageout alone is inert */
- s.free = 150000;
- engine_init(&e); engine_step(&e, &c, &s, none, rising, rising);
- CHECK(e.state == NORMAL && e.low_since < 0); /* corroboration needs low memory */
- s.free = 60000;
- engine_init(&e);
- for (i = 0; i <= 3; i++) {
- s.at = i; engine_step(&e, &c, &s, none, rising, rising);
- }
- CHECK(e.state == CRITICAL && e.danger);
- }
- puts("PASS all six states, corroboration, hysteresis, recovery and sampling gaps");
- }
- static void actions(void)
- {
- config_t c = allowed(); engine_t e; controller_t a = {0};
- process_t p = process(123); sample_t s; plan_t plan; unsigned i;
- engine_init(&e); e.state = CRITICAL; e.danger = 1;
- s = sample(100, 60000, 1);
- plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == TERM);
- action_commit(&a, &c, &s, &plan); CHECK(a.has_pending && a.nvictims == 1);
- printf("SIMULATED t=100 state=CRITICAL UUID=%s pid=123 WOULD_ACTION=SIGTERM\n", uuid);
- s.at = 104; plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == NONE);
- s.at = 105; plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == KILL);
- action_commit(&a, &c, &s, &plan); CHECK(!a.has_pending && a.nvictims == 1);
- puts("SIMULATED t=105 state=CRITICAL pid=123 WOULD_ACTION=SIGKILL reason=memory_not_recovered");
- p.pid++; s.at = 106; CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE);
- e.state = EMERGENCY; s.free = 40000;
- plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == TERM);
- action_commit(&a, &c, &s, &plan);
- s.at = 106.5; CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE);
- s.at = 107; CHECK(action_plan(&a, &c, &e, &s, &p).action == KILL);
- s.free = 20000; e.floor = 1;
- plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == KILL);
- action_commit(&a, &c, &s, &plan);
- p.pid++; s.at++;
- plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == KILL);
- action_commit(&a, &c, &s, &plan);
- puts("SIMULATED state=EMERGENCY floor=1 WOULD_ACTION=SIGKILL reason=floor");
- p.pid++; s.at++;
- plan = action_plan(&a, &c, &e, &s, &p); CHECK(plan.action == KILL);
- action_commit(&a, &c, &s, &plan);
- p.pid++; s.at++;
- CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE); /* even at floor */
- e.state = NORMAL; e.danger = 0; s.free = 160000;
- (void)action_plan(&a, &c, &e, &s, &p); CHECK(a.nvictims == 0);
- e.state = EMERGENCY; e.danger = 1; s.free = 20000;
- CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE); /* minute budget persists */
- s.at = 180; CHECK(action_plan(&a, &c, &e, &s, &p).action == KILL);
- memset(&a, 0, sizeof (a)); e.floor = 0; e.state = CRITICAL; s.free = 60000;
- plan = action_plan(&a, &c, &e, &s, &p); action_commit(&a, &c, &s, &plan);
- s.free = 140000; e.danger = 0; e.state = RECOVERY;
- CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE && !a.has_pending);
- for (i = 0; i < 2; i++) {
- c.armed = (int)i; memset(&a, 0, sizeof (a));
- e.danger = 1; e.state = EMERGENCY; e.floor = 1; s.free = 1000;
- p.zid = 0; CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE);
- p.zid = 9; c.zones[0].policy = PROTECT;
- CHECK(action_plan(&a, &c, &e, &s, &p).action == NONE);
- c.zones[0].policy = ALLOW;
- }
- puts("PASS TERM/KILL sequencing, emergency grace, floor, cooldown, budgets and no-action protections");
- }
- int main(void)
- {
- configs(); protections(); histories(); states(); actions();
- printf("PASS %u checks; no native backend linked, no signals sent\n", checks);
- return 0;
- }
|