Files
ESP32_Serial_Swiss_Army_Knife/tests/sdk_security_overrides/scratch.c
T
Commander1024 cdc9c7335a Add Phase 9C security hardening
Generate exact-hash SDK source overrides without modifying dependencies.
Harden
SSH allocation and algorithm policy, tighten web authentication cleanup,
and add
focused host contract tests and documentation.
2026-09-15 22:12:57 +02:00

120 lines
5.7 KiB
C

/* SPDX-License-Identifier: GPL-3.0-only */
#include <sys/types.h>
#include "alloc.h"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define HTTPD_SOCK_ERR_TIMEOUT -10
#define HTTPD_SOCK_ERR_FAIL -11
#define HTTPD_408_REQ_TIMEOUT 408
struct sock_db {
char pending_data[128]; size_t pending_len;
void *ctx; void (*free_ctx)(void *); bool ignore_sess_ctx_changes;
};
struct httpd_req_aux {
struct sock_db *sd;
char *scratch; size_t scratch_cur_size, scratch_size_limit, remaining_len;
};
typedef struct httpd_req {
struct httpd_req_aux *aux; void *sess_ctx, *handle, *user_ctx;
void (*free_ctx)(void *); bool ignore_sess_ctx_changes;
} httpd_req_t;
typedef struct { void *data; } http_parser;
typedef struct { struct { char *at; } last; } parser_data_t;
static int receive_result = 1, receive_calls;
static int httpd_req_handle_err(httpd_req_t *r, int err) { return ESP_FAIL; }
static void httpd_sess_free_ctx(void **ctx, void (*fn)(void *)) { assert(!*ctx); }
static int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t n, bool halt_after_pending);
/* SDK_FUNCTIONS */
static int httpd_recv_with_opt(httpd_req_t *r, char *buf, size_t n, bool halt_after_pending) {
++receive_calls;
assert(halt_after_pending);
if (r->aux->sd->pending_len) return (int)httpd_recv_pending(r, buf, n);
if (receive_result <= 0) return receive_result;
memset(buf, 'x', n); return (int)n;
}
static void cleanup(httpd_req_t *r, struct httpd_req_aux *ra, struct sock_db *sd) {
r->aux = ra; ra->sd = sd;
httpd_req_cleanup(r);
assert(!ra->scratch && !ra->scratch_cur_size && !r->aux && !live);
}
int main(void) {
all_secret = true;
struct sock_db sd = {0};
struct httpd_req_aux ra = {.sd=&sd, .scratch_size_limit=64, .remaining_len=37};
httpd_req_t r = {.aux=&ra};
parser_data_t data = {0}; http_parser parser = {.data=&data};
/* Equivalent pointer/size initialization to parse_init/init_req_aux:
* the first read must allocate its own scratch, with no parser position. */
assert(!data.last.at && !ra.scratch && !ra.scratch_cur_size);
unsigned initial_wipes = wiped_frees;
int initial_reads = receive_calls;
fail_at = calls + 1;
assert(read_block(&r, &parser, 0, 8) == 0);
assert(!data.last.at && !ra.scratch && !ra.scratch_cur_size && !live);
assert(receive_calls == initial_reads && wiped_frees == initial_wipes);
cleanup(&r, &ra, &sd);
assert(wiped_frees == initial_wipes);
fail_at = 0;
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
assert(read_block(&r, &parser, 0, 8) == 8);
assert(ra.scratch && ra.scratch_cur_size == 8 && !data.last.at);
assert(receive_calls == initial_reads + 1 && wiped_frees == initial_wipes);
assert(!memcmp(ra.scratch, "xxxxxxxx", 8));
/* A fragmented request can need another read before the URL callback. */
char *initial = ra.scratch;
fail_at = calls + 1;
assert(read_block(&r, &parser, 8, 8) == 0);
assert(ra.scratch == initial && ra.scratch_cur_size == 8 && !data.last.at);
assert(receive_calls == initial_reads + 1 && wiped_frees == initial_wipes);
fail_at = 0;
assert(read_block(&r, &parser, 8, 8) == 8);
assert(ra.scratch != initial && ra.scratch_cur_size == 16 && !data.last.at);
assert(!memcmp(ra.scratch, "xxxxxxxxxxxxxxxx", 16));
assert(wiped_frees == initial_wipes + 1);
cleanup(&r, &ra, &sd);
assert(wiped_frees == initial_wipes + 2);
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
assert(read_block(&r, &parser, 0, 16) == 16);
assert(!data.last.at);
memcpy(ra.scratch, "Cookie: secret!!", 16);
data.last.at = ra.scratch + 7;
char *old = ra.scratch; unsigned before = wiped_frees;
assert(read_block(&r, &parser, 16, 8) == 8);
assert(ra.scratch != old && ra.scratch_cur_size == 24);
assert(!memcmp(ra.scratch, "Cookie: secret!!", 16));
assert(data.last.at == ra.scratch + 7 && wiped_frees == before + 1);
old = ra.scratch; unsigned saved_calls = calls;
assert(security_override_resize_scratch(&ra, 24) && ra.scratch == old && calls == saved_calls);
fail_at = calls + 1;
int reads = receive_calls;
assert(read_block(&r, &parser, 24, 8) == 0);
assert(ra.scratch == old && ra.scratch_cur_size == 24 && receive_calls == reads);
assert(data.last.at == old + 7 && !memcmp(old, "Cookie: secret!!", 16));
assert(ra.remaining_len == 37);
fail_at = 0;
assert(httpd_unrecv(&r, "NEXT-REQUEST", 12) == 12);
assert(read_block(&r, &parser, 4, 4) == 4); /* actual shrink + pending RX */
assert(ra.scratch_cur_size == 8 && !memcmp(ra.scratch, "CookNEXT", 8));
assert(sd.pending_len == 8 && !memcmp(sd.pending_data + 120, "-REQUEST", 8));
assert(ra.remaining_len == 37);
char pending[128]; memcpy(pending, sd.pending_data, sizeof(pending));
cleanup(&r, &ra, &sd);
assert(sd.pending_len == 8 && !memcmp(pending, sd.pending_data, sizeof(pending)));
r.aux = &ra; ra.sd = &sd;
char out[12] = {0}; assert(httpd_recv_pending(&r, out, 8) == 8 && !memcmp(out, "-REQUEST", 8));
ra.scratch_size_limit = 64;
fail_at = calls + 1;
assert(!security_override_resize_scratch(&ra, 8) && !ra.scratch);
cleanup(&r, &ra, &sd); fail_at = 0;
for (int result = 0; result >= -2; --result) {
r.aux = &ra; ra.sd = &sd; ra.scratch_size_limit = 64;
assert(security_override_resize_scratch(&ra, 8)); data.last.at = ra.scratch;
receive_result = result == -2 ? HTTPD_SOCK_ERR_TIMEOUT : result;
assert(read_block(&r, &parser, 0, 8) == HTTPD_SOCK_ERR_FAIL);
int before_reads = receive_calls;
assert(read_block(&r, &parser, 64, 1) == 0 && before_reads == receive_calls);
cleanup(&r, &ra, &sd);
}
puts("HTTPD null-initial read/grow/shrink/failure/final wipe/bounds/pending-unread matrix PASS");
}