Skip to content

Commit df3c753

Browse files
committed
hardening: simplify plan↔gate matching (bare sysctl key)
The orchestrator now publishes the bare sysctl name as the plan knob (e.g. "perf_event_paranoid"), matching the renderer's gate.name exactly, instead of a formatted "kernel.<name>>=<n>" string the renderer had to re-parse. The text and JSON renderers share one hardening_bits_for() exact-match helper, replacing the duplicated prefix-match in each. Name the plan's working-buffer caps.
1 parent ae7baaf commit df3c753

2 files changed

Lines changed: 34 additions & 23 deletions

File tree

src/orchestrator.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,8 +2399,10 @@ static int engine_quantity_bits(const struct engine *e, enum kasld_quantity q,
23992399
return slots > 0 ? ilog2(slots) : 0;
24002400
}
24012401

2402-
/* Map a contributing component origin to its hardening knob — the sysctl a
2403-
* defender would restrict. Falls back to the origin itself when the component
2402+
/* Map a contributing component origin to its hardening knob KEY — the bare
2403+
* sysctl name a defender would restrict (e.g. "perf_event_paranoid"), which
2404+
* matches the renderer's gate.name exactly, so no formatted string has to be
2405+
* re-parsed downstream. Falls back to the origin itself when the component
24042406
* declares no sysctl gate (side channels, generic readers): that channel is its
24052407
* own knob. */
24062408
static void origin_knob(const char *origin, char *out, size_t outlen) {
@@ -2409,7 +2411,12 @@ static void origin_knob(const char *origin, char *out, size_t outlen) {
24092411
continue;
24102412
const char *s = meta_get(&comp_logs[i].meta, "sysctl");
24112413
if (s && *s) {
2412-
snprintf(out, outlen, "kernel.%.80s", s);
2414+
/* meta value is "<name>>=<n>"; the key is the bare name (up to '>'). */
2415+
size_t n = strcspn(s, ">");
2416+
if (n >= outlen)
2417+
n = outlen - 1;
2418+
memcpy(out, s, n);
2419+
out[n] = '\0';
24132420
return;
24142421
}
24152422
break;
@@ -2427,6 +2434,11 @@ static void origin_knob(const char *origin, char *out, size_t outlen) {
24272434
* "bits restored" is exact. Per-knob with all others held open (a redundant
24282435
* knob reads +0 until the knob dominating it is closed); diagnostic to stderr.
24292436
*/
2437+
/* Working caps for the plan computation: distinct knobs/origins considered, and
2438+
* the knob-key buffer length. Safe ceilings — the published plan is capped
2439+
* separately at MAX_HARDENING_PLAN. */
2440+
enum { CF_MAX_KNOBS = 256, CF_KNOB_LEN = 96 };
2441+
24302442
static void engine_report_hardening_plan(const struct engine *base) {
24312443
if (!hardening_mode)
24322444
return;
@@ -2443,10 +2455,10 @@ static void engine_report_hardening_plan(const struct engine *base) {
24432455
#endif
24442456

24452457
/* Distinct contributing origins and the knob each maps to. */
2446-
static char origins[256][ORIGIN_LEN];
2447-
static char oknob[256][96];
2458+
static char origins[CF_MAX_KNOBS][ORIGIN_LEN];
2459+
static char oknob[CF_MAX_KNOBS][CF_KNOB_LEN];
24482460
int no = 0;
2449-
for (int i = 0; i < base->ev.n_obs && no < 256; i++) {
2461+
for (int i = 0; i < base->ev.n_obs && no < CF_MAX_KNOBS; i++) {
24502462
const char *o = base->ev.obs[i].origin;
24512463
if (!o[0])
24522464
continue;
@@ -2464,7 +2476,7 @@ static void engine_report_hardening_plan(const struct engine *base) {
24642476
}
24652477

24662478
/* Distinct knobs. */
2467-
static char knobs[256][96];
2479+
static char knobs[CF_MAX_KNOBS][CF_KNOB_LEN];
24682480
int nk = 0;
24692481
for (int i = 0; i < no; i++) {
24702482
int seen = 0;
@@ -2485,7 +2497,7 @@ static void engine_report_hardening_plan(const struct engine *base) {
24852497
const char *knob;
24862498
int dv, dp, ncomp;
24872499
};
2488-
struct cf_plan plan[256];
2500+
struct cf_plan plan[CF_MAX_KNOBS];
24892501
int np = 0;
24902502

24912503
for (int k = 0; k < nk; k++) {
@@ -2497,7 +2509,7 @@ static void engine_report_hardening_plan(const struct engine *base) {
24972509
*cf = *base; /* copy evidence + state; engine_run_full re-resolves fresh */
24982510
int w = 0;
24992511
for (int j = 0; j < cf->ev.n_obs; j++) {
2500-
char kn[96];
2512+
char kn[CF_KNOB_LEN];
25012513
origin_knob(cf->ev.obs[j].origin, kn, sizeof(kn));
25022514
if (strcmp(kn, knobs[k]) == 0)
25032515
continue; /* this knob silences this observation */

src/render/hardening.c

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ static int has_mitigation_keys(const struct component_meta *m) {
7272
return 0;
7373
}
7474

75+
/* KASLR entropy (bits) restored if the named sysctl knob is closed, from the
76+
* counterfactual plan the orchestrator publishes; -1 when the plan has no entry
77+
* (closing it yields no measured gain). Keyed by the bare sysctl name, matching
78+
* gate.name. */
79+
static int hardening_bits_for(const char *sysctl_name) {
80+
for (int i = 0; i < hardening_plan_count; i++)
81+
if (strcmp(hardening_plan[i].knob, sysctl_name) == 0)
82+
return hardening_plan[i].virt_bits + hardening_plan[i].phys_bits;
83+
return -1;
84+
}
85+
7586
void render_hardening_text(void) {
7687
printf("\n%s========================================%s\n", c(C_BOLD),
7788
c(C_RESET));
@@ -284,13 +295,7 @@ void render_hardening_text(void) {
284295
gated++;
285296
if (gated == 0)
286297
continue;
287-
int bits = -1; /* match the plan knob by its display prefix */
288-
size_t dlen = strlen(gates[g].display);
289-
for (int p = 0; p < hardening_plan_count; p++)
290-
if (strncmp(hardening_plan[p].knob, gates[g].display, dlen) == 0) {
291-
bits = hardening_plan[p].virt_bits + hardening_plan[p].phys_bits;
292-
break;
293-
}
298+
int bits = hardening_bits_for(gates[g].name);
294299
snprintf(sg[nsg].action, sizeof(sg[nsg].action), "Set %s = %d",
295300
gates[g].display, gates[g].threshold);
296301
snprintf(sg[nsg].detail, sizeof(sg[nsg].detail), "affects %d component%s",
@@ -757,13 +762,7 @@ void render_hardening_json(void) {
757762
printf(",\n");
758763
first_sug = 0;
759764

760-
int bits = -1; /* KASLR entropy restored, matched from the plan by knob */
761-
size_t dlen = strlen(gates[g].display);
762-
for (int p = 0; p < hardening_plan_count; p++)
763-
if (strncmp(hardening_plan[p].knob, gates[g].display, dlen) == 0) {
764-
bits = hardening_plan[p].virt_bits + hardening_plan[p].phys_bits;
765-
break;
766-
}
765+
int bits = hardening_bits_for(gates[g].name);
767766

768767
printf(" {\n");
769768
printf(" \"action\": \"Set %s = %d\",\n", gates[g].display,

0 commit comments

Comments
 (0)