make the guard a gate: sha256_hex(50000) no longer segfaults
This commit is contained in:
@@ -712,6 +712,20 @@ el_val_t el_map_set(el_val_t mapv, el_val_t keyv, el_val_t value) {
|
||||
* happen to look like aligned heap pointers are exceedingly unlikely to land
|
||||
* on a page whose first 4 bytes match either magic. */
|
||||
|
||||
/* el_tagged — THE gate for "is this slot a heap object carrying this tag".
|
||||
*
|
||||
* el_val_t carries both integers and tagged heap pointers, so deciding which
|
||||
* requires checking the value BEFORE dereferencing it. That check was a
|
||||
* convention every author had to know rather than a gate they had to pass
|
||||
* through, and the result is measurable: geom_of and mfld_of call
|
||||
* looks_like_heap_obj and are correct; el_bin_lookup checked only a 4096 floor
|
||||
* -- no alignment, no small-int, no negative -- and reads EIGHT BYTES BACKWARD
|
||||
* from the pointer. sha256_hex(50000) therefore compiled clean and segfaulted.
|
||||
*
|
||||
* Exported, so the engram siblings stop re-deriving it. Anything that
|
||||
* dereferences a slot without passing through here is the defect. */
|
||||
int el_tagged(el_val_t v, uint32_t magic);
|
||||
|
||||
static int looks_like_heap_obj(el_val_t v) {
|
||||
if (v == 0) return 0;
|
||||
int64_t s = (int64_t)v;
|
||||
@@ -722,6 +736,12 @@ static int looks_like_heap_obj(el_val_t v) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int el_tagged(el_val_t v, uint32_t magic) {
|
||||
if (!looks_like_heap_obj(v)) return 0;
|
||||
return *(const uint32_t*)(uintptr_t)v == magic;
|
||||
}
|
||||
|
||||
|
||||
void el_retain(el_val_t v) {
|
||||
if (!looks_like_heap_obj(v)) return;
|
||||
ElHeader* h = (ElHeader*)(uintptr_t)v;
|
||||
@@ -18272,8 +18292,11 @@ static int el_bin_lookup(const void* p, size_t* out_len) {
|
||||
/* Avoid reading off the front of a page on tiny pointers (e.g. NULs
|
||||
* passed in as int-cast values). 4096 is a safe lower bound on any
|
||||
* platform we target. */
|
||||
if ((uintptr_t)p < 4096) return 0;
|
||||
const el_bin_hdr_t* hdr = (const el_bin_hdr_t*)((const char*)p - sizeof(el_bin_hdr_t));
|
||||
/* Reads BACKWARD, so the HEADER address is what must be validated -- and a
|
||||
* 4096 floor alone let sha256_hex(50000) through to a SIGSEGV. */
|
||||
const char* hp = (const char*)p - sizeof(el_bin_hdr_t);
|
||||
if (!looks_like_heap_obj((el_val_t)(uintptr_t)hp)) return 0;
|
||||
const el_bin_hdr_t* hdr = (const el_bin_hdr_t*)hp;
|
||||
if (hdr->magic != EL_MAGIC_BIN) return 0;
|
||||
*out_len = hdr->length;
|
||||
return 1;
|
||||
@@ -18283,7 +18306,12 @@ static int el_bin_lookup(const void* p, size_t* out_len) {
|
||||
static size_t el_input_len(const char* s) {
|
||||
size_t n;
|
||||
if (el_bin_lookup(s, &n)) return n;
|
||||
return s ? strlen(s) : 0;
|
||||
/* The FALLBACK is the hazard, not the tagged lookup. A NULL check does not
|
||||
* establish that a slot is a pointer: el_val_t carries integers too, so
|
||||
* strlen() on `sha256_hex(50000)` walks address 50000. Guarding the tagged
|
||||
* path alone left this untouched and the SIGSEGV unchanged -- measured. */
|
||||
if (!looks_like_heap_obj((el_val_t)(uintptr_t)s)) return 0;
|
||||
return strlen(s);
|
||||
}
|
||||
|
||||
/* ─── SHA-256 (Brad Conte / public domain) ──────────────────────────────── */
|
||||
|
||||
@@ -888,6 +888,7 @@ el_val_t engram_age_field_catchup(void);
|
||||
el_val_t engram_chrono_persist_tick(void);
|
||||
el_val_t engram_chrono_tick(void);
|
||||
el_val_t engram_boundary_beat(el_val_t op_name, el_val_t construct);
|
||||
int el_tagged(el_val_t v, uint32_t magic); /* the gate: validate a slot BEFORE dereferencing it */
|
||||
el_val_t el_seam_run(el_val_t fn_name, el_val_t phase, el_val_t result); /* runtime construct seam */
|
||||
el_val_t el_seam_wrap(el_val_t fn_name, el_val_t (*body)(void*), void* env); /* runtime invocation control */ /* API-reshape decorator-seam auto-emit; construct = the decorator that caused the beat */
|
||||
el_val_t engram_self_anchor_capture(void);
|
||||
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env bash
|
||||
# tagged_gate.sh — a slot must be validated before it is dereferenced.
|
||||
#
|
||||
# el_val_t carries both integers and tagged heap pointers, so "is this a
|
||||
# pointer" is undecidable without checking first. That check was a CONVENTION
|
||||
# every author had to know rather than a GATE they had to pass through:
|
||||
# geom_of, mfld_of call looks_like_heap_obj correct
|
||||
# el_bin_lookup checked only a 4096 floor read 8 bytes backward
|
||||
# el_input_len checked only for NULL strlen'd an integer
|
||||
# sha256_hex(50000) therefore compiled clean and segfaulted (exit 139).
|
||||
set -uo pipefail
|
||||
ELC="${1:?usage: tagged_gate.sh <elc>}"
|
||||
LANG_DIR="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||
W=$(mktemp -d); trap 'rm -rf "$W"' EXIT; F=0
|
||||
chk(){ [ "$2" = "$3" ] && printf ' ok %s\n' "$1" || { printf ' FAIL %s\n expected %s got %s\n' "$1" "$2" "$3"; F=$((F+1)); }; }
|
||||
cd "$LANG_DIR"
|
||||
SRCS=$(../scripts/el-runtime-sources.sh runtime)
|
||||
CF="-std=c11 -O2 -I runtime"; for d in /opt/homebrew/opt/openssl@3 /usr/local/opt/openssl@3; do [ -d "$d" ] && CF="$CF -I $d/include" && LF="-L $d/lib"; done
|
||||
LF="${LF:-} -lcurl -lssl -lcrypto -lpthread -lm"
|
||||
|
||||
build(){ "$ELC" "$1" > "$W/t.c" 2>/dev/null && cc $CF -o "$W/t" "$W/t.c" $SRCS $LF 2>/dev/null; }
|
||||
|
||||
printf 'fn main() { let h: String = sha256_hex(50000) println("got " + h) }\n' > "$W/a.el"
|
||||
build "$W/a.el"; "$W/t" >"$W/o" 2>&1; chk "an integer where a string is expected does not crash" "0" "$?"
|
||||
chk "and yields the empty-string hash, not memory" "1" "$(grep -c e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 "$W/o")"
|
||||
|
||||
printf 'fn main() { let h: String = sha256_hex(-5) println("got " + h) }\n' > "$W/b.el"
|
||||
build "$W/b.el"; "$W/t" >/dev/null 2>&1; chk "a NEGATIVE integer does not crash" "0" "$?"
|
||||
|
||||
printf 'fn main() { println(sha256_hex("abc")) }\n' > "$W/c.el"
|
||||
build "$W/c.el"; out=$("$W/t" 2>&1)
|
||||
chk "a legitimate string still hashes correctly" "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "$out"
|
||||
|
||||
chk "the gate is exported, so siblings stop re-deriving it" "1" "$(grep -c 'int *el_tagged(el_val_t' runtime/el_runtime.h)"
|
||||
echo; echo " 5 assertions, $((5-F)) passed, $F failed"; exit $F
|
||||
Reference in New Issue
Block a user