c6ba0677f0
cycles/ one file per Ishikawa -> scientific method -> Six Sigma loop, named
for the DEFECT not the fix, carrying the commit record as written at
the time
findings/ what the cycles produced, cross-cut: live bugs, architecture answers,
and defects in my own measurement
The organising finding is that predictions which came back FALSE produced every
significant result. Eleven of sixty-one failed, and those eleven found: that the
arity table was not drifted but 40% incomplete; that the AST traversal is
irreducible and only rules and judgments move; that guards could refuse through
the seam after all; and that routing el_bin_lookup through the gate did NOT fix
the SIGSEGV, because the fallback strlen was the hazard -- a wrong fix I would
otherwise have shipped as verified.
One cycle was run without committing predictions first and had to be discarded
as rigged. It is kept, in full, as 18-async-half-expressible.md.
75 lines
2.5 KiB
Markdown
75 lines
2.5 KiB
Markdown
# Live defects found
|
|
|
|
Every one compiled clean, ran, and produced a wrong result or a crash with **no
|
|
diagnostic at any layer**. All four were present before this session; none was
|
|
introduced by it.
|
|
|
|
## Silent miscompilations
|
|
|
|
### 1. An unannotated `let` loses its type
|
|
|
|
```el
|
|
let a = str_len("hello") // no annotation
|
|
let b = str_len("hi")
|
|
let c = a + b // → el_str_concat(a, b) on two integers
|
|
```
|
|
|
|
Compiled clean. Printed **nothing** where it should print 7. Fixed: an
|
|
unannotated `let` takes its type from what its initialiser returns. The return
|
|
types were already required for dispatch and were simply never consulted at the
|
|
binding site.
|
|
|
|
### 2. Reserved keywords that reserved nothing
|
|
|
|
```el
|
|
let seed = 42
|
|
let impl = seed + 1
|
|
```
|
|
|
|
`sealed`, `activate`, `seed`, `protocol`, `impl` were keywords in the lexer and
|
|
consumed by no parser or codegen path. Using one did not fail to parse — it
|
|
compiled clean, with zero `cc` errors, and printed **0 instead of 44**. Fixed by
|
|
removing all five.
|
|
|
|
### 3. `Instant + Int` was never refused
|
|
|
|
```el
|
|
let t: Instant = now()
|
|
let u: Instant = t + 3 // → (t + 3), reported clean
|
|
```
|
|
|
|
`Duration + Int` was refused — *"an Int carries no unit"* — while adding a
|
|
dimensionless number to a **point** silently moved the instant by an
|
|
unspecified amount. Three of *what*? Whatever the representation happens to be.
|
|
The rule was simply never written.
|
|
|
|
## Security-relevant
|
|
|
|
### 4. Annotations are never verified
|
|
|
|
```el
|
|
let x: Int = "hello"; x + 1 → 4343631981 a string POINTER used as an integer
|
|
let s: String = 42; println(s) → nothing address 42 dereferenced
|
|
```
|
|
|
|
The first **leaks a raw memory address into program output**. The second is an
|
|
**arbitrary-read primitive** if the integer is ever attacker-influenced.
|
|
|
|
### 5. `sha256_hex(<integer>)` segfaults
|
|
|
|
```el
|
|
let h: String = sha256_hex(50000) → exit 139, SIGSEGV
|
|
```
|
|
|
|
Compiled clean. `el_bin_lookup` checked only a 4096 floor — no alignment, no
|
|
small-int, no negative — and reads **eight bytes backward** from the pointer.
|
|
And the actual crash was one level further on: `el_input_len` fell through to
|
|
`strlen()` on address 50000, because a NULL check does not establish that a slot
|
|
is a pointer.
|
|
|
|
Fixed, and the guard is now a **gate**: `el_tagged()` is exported in
|
|
`el_runtime.h`. `geom_of` and `mfld_of` were always correct because their authors
|
|
knew to call `looks_like_heap_obj`; `el_bin_lookup` and `el_input_len` were wrong
|
|
because theirs did not, and the function was `static`, so every sibling
|
|
translation unit re-derived it.
|