Просмотр исходного кода

fix: long-line buffer overflow (the 7 'set' segfaults)

PrintListing's line buffer was ARRAY [0..255] and GetLine wrote without
bounding to HIGH(line), so a source line > 255 chars (e.g. an 860-char
enum line in the gm2 set tests) overflowed the stack and crashed M2.

- compiler.frm GetLine: truncate at HIGH(line) (still consume the
  rest of the line so positions stay correct);
- PrintListing line buffer -> ARRAY [0..4095];
- PrintErr caret loop bounded by HIGH(line).
- regression test t_longline.mod (406-char line, exit 0).
- corpus: 7 CRASH -> 0 (219 OK / 0 CRASH / 187 LIB / 206 LANG).
- topspeed README: stale 631 -> 644.
Suite 152/152; fixpoint OK (2,339,115 bytes).
Eric Streit 1 неделя назад
Родитель
Сommit
7744437f77

+ 1 - 0
compiler/run_tests.sh

@@ -84,6 +84,7 @@ expect_run t_valconv.mod 15
 expect_run t_classsibling.mod 7
 expect_run t_classinit.mod 42
 expect_run t_class.mod 0
+expect_run t_longline.mod 0
 expect_fail t_bad_parent.mod "undeclared identifier"
 expect_run showcase3.mod 183
 expect_run showcase4.mod 44

+ 8 - 3
compiler/src/compiler.frm

@@ -63,7 +63,10 @@ MODULE -->Grammar;
       BEGIN
         i := 0; eof := FALSE; ch := CharAt(pos); INC(pos);
         WHILE (ch # CR) AND (ch # LF) AND (ch # EOF) DO
-          line[i] := ch; INC(i); ch := CharAt(pos); INC(pos);
+          (* bound-check: a line longer than the caller's buffer must
+             truncate, not overflow it (stack corruption / segfault) *)
+          IF i < HIGH(line) THEN line[i] := ch; INC(i) END;
+          ch := CharAt(pos); INC(pos);
         END;
         eof := (i = 0) AND (ch = EOF); line[i] := CHR(0);
         IF ch = CR THEN (* check for MsDos *)
@@ -122,7 +125,9 @@ MODULE -->Grammar;
         BEGIN
           WriteString(lst, "*****  ");
           i := 0;
-          WHILE i < col + Extra - 2 DO
+          WHILE (i < col + Extra - 2)
+            AND (i <= VAL(INTEGER, HIGH(line)))
+            AND (line[i] # CHR(0)) DO
             IF line[i] = tab
               THEN Write(lst, tab)
               ELSE Write(lst, ' ')
@@ -145,7 +150,7 @@ MODULE -->Grammar;
         eof, done: BOOLEAN;
         lnr, errC: INTEGER;
         srcPos: INT32;
-        line: ARRAY [0 .. 255] OF CHAR;
+        line: ARRAY [0 .. 4095] OF CHAR;   (* long lines (e.g. big enums) *)
       BEGIN
         WriteString(lst, "Listing:");
         WriteLn(lst); WriteLn(lst);

+ 8 - 0
compiler/tests/t_longline.mod

@@ -0,0 +1,8 @@
+MODULE TLongLine;
+(* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx *)
+(* A source line longer than the old 256-byte listing buffer must
+   truncate, not overflow the stack (the set-file segfault). *)
+VAR ExitCode : INTEGER;
+BEGIN
+  ExitCode := 0
+END TLongLine.

+ 43 - 22
docs/summary_v3-corpus.md

@@ -16,31 +16,52 @@ Harness: `tools/v3-corpus/corpus.sh` (copies each file to a scratch
 tree, compiles alone, splits failures into CRASH / LIB / LANG — see its
 README).
 
-## Baseline
+## Baseline (after the crash fix)
 
 | | files |
 | --- | --- |
 | total | **612** |
 | **compile-OK** (0 errors) | **219** |
-| CRASH (M2 died, rc ≥ 128) | **7** |
-| LIB (imports a gm2 library V3 lacks) | **182** |
-| LANG (real V3 gap) | **204** |
+| CRASH (M2 died, rc ≥ 128) | **0** |
+| LIB (imports a gm2 library V3 lacks) | **187** |
+| LANG (real V3 gap) | **206** |
 
-So ~182 of the 393 failures are simply **missing gm2 libraries**
-(`StrIO`, `NumberIO`, `STextIO`, `SWholeIO`, `FpuIO`, `WholeStr`,
-`StdIO`, `M2RTS`, `DynamicStrings`, `Processes`, …) — expected, not
-language gaps.  The signal is the **204 LANG** files.
+(Before the fix: 219 OK / **7 CRASH** / 182 LIB / 204 LANG.)  So ~187
+of the 393 failures are simply **missing gm2 libraries** (`StrIO`,
+`NumberIO`, `STextIO`, `SWholeIO`, `FpuIO`, `WholeStr`, `StdIO`,
+`M2RTS`, `DynamicStrings`, `Processes`, …) — expected, not language
+gaps.  The signal is the **206 LANG** files.
 
-## Top LANG gaps (from the corpus)
+## Fix: long-line buffer overflow (the 7 "set crashes")
+
+The 7 crashes (`largeset`, `program2`, `set9`, `sets3`,
+`smallset4/5/6`) were **not** set bugs: those files contain a ~860-char
+line (a 76-literal enumeration), and
+
+- `PrintListing`'s line buffer was `ARRAY [0 .. 255] OF CHAR`, and
+- `GetLine` wrote **without bounding** to `HIGH(line)`.
+
+A source line longer than 255 chars therefore **overflowed the stack**
+(corrupting locals and the return address) — hence the garbage line
+number in the `.LST` and the segfault.
+
+Fixed in `compiler/src/compiler.frm`:
+- `GetLine` now truncates at `HIGH(line)` (still consuming the rest of
+  the line so positions stay correct);
+- `PrintListing`'s buffer is now `ARRAY [0 .. 4095] OF CHAR`;
+- `PrintErr`'s caret loop is bounded by `HIGH(line)`.
+
+Regression test: `compiler/tests/t_longline.mod` (406-char line, exit
+0).  Suite **152/152**; fixpoint OK (2,339,115 bytes).
+
+## Top remaining LANG gaps (from the corpus)
 
-- **7 crashes** — all set-related: `largeset`, `program2`, `set9`,
-  `sets3`, `smallset4/5/6`.  Real bugs (segfaults), highest priority.
 - **48 `not supported yet`** — deliberate V3 `230`s; the corpus shows
   which constructs matter, e.g. **array constructors**
   (`array {'h','e','l','l','o'}` in `arrayconst1`).
-- **79 `undeclared identifier`** — mostly **`SYSTEM` facilities** V3
-  does not expose (`FROM SYSTEM IMPORT BYTE;` → `BYTE`), plus small
-  library holes.
+- **`undeclared identifier`** — mostly **`SYSTEM` facilities** V3 does
+  not expose (`FROM SYSTEM IMPORT BYTE;` → `BYTE`), plus small library
+  holes.
 - **Anchored subranges** `INTEGER [-1 .. 24]` (`array2`, `array3`) —
   PIM form V3's `Subrange` does not parse (`';' expected` at `[`).
 - **Unnamed parameters in procedure types**
@@ -53,19 +74,19 @@ language gaps.  The signal is the **204 LANG** files.
 
 ```sh
 cd tools/v3-corpus
-./corpus.sh            # -> 219 compile-OK / 7 CRASH / 182 LIB / 204 LANG
+./corpus.sh            # -> 219 compile-OK / 0 CRASH / 187 LIB / 206 LANG
 ```
 
 ## Suggested next steps
 
-1. **Fix the 7 set crashes** (a correctness bug — a compiler must not
-   segfault on valid input).
-2. **`SYSTEM` facilities**: expose at least `BYTE` / `WORD` /
+1. **`SYSTEM` facilities**: expose at least `BYTE` / `WORD` /
    `SHORTADDR` etc. so `FROM SYSTEM IMPORT …` works.
-3. **Anchored subranges** `T[lo..hi]` (small grammar addition).
-4. **Unnamed proc-type parameters** (small grammar addition).
-5. **Array constructors** `array{…}` (bigger: codegen).
+2. **Anchored subranges** `T[lo..hi]` (small grammar addition).
+3. **Unnamed proc-type parameters** (small grammar addition).
+4. **Array constructors** `array{…}` (bigger: codegen).
 
 ## Files
 
-`tools/v3-corpus/{corpus.sh,README.md}`, this doc.
+`tools/v3-corpus/{corpus.sh,README.md}`,
+`compiler/src/compiler.frm` (long-line fix),
+`compiler/tests/t_longline.mod`, `compiler/run_tests.sh`, this doc.

+ 1 - 1
tools/topspeed-grammar/README.md

@@ -43,5 +43,5 @@ so the `.LST` listings never touch the corpus, and reports:
 - **syntax-bad** — a real Coco/R error (`'x' expected`, `invalid X`).
 
 Current result on the TS-V3 tree (662 files, 352 `.DEF` + 310 `.MOD`):
-**631 parse-OK / 31 syntax-bad** — see
+**644 parse-OK / 18 syntax-bad** — see
 `docs/summary_topspeed-corpus.md`.