Parcourir la source

fix(fileio): shim file routines are NULL-handle safe; Showcase11 no longer SIGSEGVs on a failed open (107/107)

Eric Streit il y a 2 semaines
Parent
commit
035d9eff51
4 fichiers modifiés avec 66 ajouts et 3 suppressions
  1. 1 0
      compiler/run_tests.sh
  2. 17 0
      compiler/tests/t_badfile.mod
  3. 35 0
      docs/summary_fix-fileio-null.md
  4. 13 3
      runtime/syslib/shim.c

+ 1 - 0
compiler/run_tests.sh

@@ -237,6 +237,7 @@ expect_run_files_in() {
 expect_run_files_in ReadProg 0 "42" "20 22" ../stdlib/sysio.def ../stdlib/sysio.mod read_prog.mod
 expect_run_files_in WholeIOProg 0 "42" "6 7" ../stdlib/sysio.def ../stdlib/sysio.mod ../stdlib/wholeio.def ../stdlib/wholeio.mod wholeio_prog.mod
 expect_run_files FilesProg 42 ../stdlib/files.def ../stdlib/files.mod files_prog.mod
+expect_run_files TBadFile 42 ../stdlib/files.def ../stdlib/files.mod t_badfile.mod
 rm -f gen_ssa/_files_tmp.txt
 expect_run_files_in Showcase11 80 "Modula-2" "42" \
   ../stdlib/sysio.def ../stdlib/sysio.mod \

+ 17 - 0
compiler/tests/t_badfile.mod

@@ -0,0 +1,17 @@
+MODULE TBadFile;
+// A failed file open returns NIL; file operations on it must degrade
+// safely (no SIGSEGV). The path below cannot be opened. Exit 42.
+IMPORT Files;
+VAR ExitCode : INTEGER;
+VAR f : Files.File;
+VAR n : INTEGER;
+BEGIN
+  f := Files.OpenWrite("no/such/dir/_t_badfile.txt");
+  Files.WriteChar(f, 'x');      (* NIL handle: must be a no-op *)
+  Files.WriteInt(f, 123);
+  Files.Close(f);
+  f := Files.OpenRead("no/such/dir/_t_badfile.txt");
+  n := Files.ReadInt(f);        (* NIL handle: must return 0 *)
+  Files.Close(f);
+  ExitCode := 42
+END TBadFile.

+ 35 - 0
docs/summary_fix-fileio-null.md

@@ -0,0 +1,35 @@
+# Fix — file I/O NULL-handle crash (`Showcase11` SIGSEGV)
+
+## Symptom
+
+`Showcase11` appeared to segfault "when entering an integer". The
+crash was actually in the `Files` section that runs just after: the
+program writes/reads `gen_ssa/_showcase11.txt` **relative to the
+current directory**. Run from anywhere lacking `gen_ssa/` (repo root,
+or from inside `gen_ssa/`), `Files.OpenWrite` returns Modula-2 `NIL`
+(a C `FILE*` of `0`), and the shim then called `fputc(NULL)` /
+`getc(NULL)` → SIGSEGV (exit 139).
+
+## Fix
+
+`runtime/syslib/shim.c`: every file routine now treats handle `0` as a
+safe no-op / returns `0` — `m2fgetc`, `m2fputc`, `m2fputs`,
+`m2freadint`, `m2fwriteint`, `m2fwriteln`, `m2fread`,
+`m2fwriteintw` (`m2fclose` was already guarded). A failed open now
+degrades gracefully instead of crashing.
+
+## Result
+
+- Run from `compiler/`: `Showcase11` unchanged (exit **80**).
+- Run elsewhere: no crash (prints `0` for the unreadable file, exit 38).
+- New regression test `compiler/tests/t_badfile.mod`: opens a path
+  that cannot exist, then does `WriteChar`/`WriteInt`/`ReadInt`/
+  `Close` on the NIL handle; expects exit 42. Registered as
+  `TBadFile` in `run_tests.sh`.
+
+Suite **107/107**.
+
+## Files
+
+`runtime/syslib/shim.c`, `compiler/tests/t_badfile.mod`,
+`compiler/run_tests.sh`.

+ 13 - 3
runtime/syslib/shim.c

@@ -86,21 +86,27 @@ long m2fclose(long h)
     return 0;
 }
 
+/* A handle of 0 means the open failed (Modula-2 NIL). Every file
+   operation degrades to a safe no-op instead of dereferencing NULL. */
+
 long m2fgetc(long h)
 {
-    int c = fgetc((FILE *)h);
+    int c;
+    if (h == 0) return 0;
+    c = fgetc((FILE *)h);
     return (c == EOF) ? 0 : c;
 }
 
 long m2fputc(long h, long c)
 {
-    fputc((int)c, (FILE *)h);
+    if (h != 0) fputc((int)c, (FILE *)h);
     return 0;
 }
 
 long m2fputs(long h, long *desc)
 {
     long n = desc[0];
+    if (h == 0) return 0;
     if (n > 0) fwrite((char *)(desc + 1), 1, (size_t)n, (FILE *)h);
     return n;
 }
@@ -109,6 +115,7 @@ long m2freadint(long h)
 {
     long v = 0;
     int c, neg = 0;
+    if (h == 0) return 0;
     do { c = fgetc((FILE *)h); } while (c == ' ' || c == '\t' ||
                                         c == '\n' || c == '\r');
     if (c == '-') { neg = 1; c = fgetc((FILE *)h); }
@@ -124,6 +131,7 @@ long m2fwriteint(long h, long v)
     char buf[32];
     int i = 0, neg = 0;
     unsigned long u;
+    if (h == 0) return 0;
     if (v < 0) { neg = 1; u = (unsigned long)(-v); }
     else u = (unsigned long)v;
     do { buf[i++] = (char)('0' + u % 10); u /= 10; } while (u != 0);
@@ -134,7 +142,7 @@ long m2fwriteint(long h, long v)
 
 long m2fwriteln(long h)
 {
-    fputc('\n', (FILE *)h);
+    if (h != 0) fputc('\n', (FILE *)h);
     return 0;
 }
 
@@ -144,6 +152,7 @@ long m2fread(long h, long *desc, long max)
 {
     long n;
     if (max < 0) max = 0;
+    if (h == 0) { desc[0] = 0; return 0; }
     n = (long)fread((char *)(desc + 1), 1, (size_t)max, (FILE *)h);
     desc[0] = n;
     return n;
@@ -155,6 +164,7 @@ long m2fwriteintw(long h, long v, long wid)
 {
     char buf[64];
     int n;
+    if (h == 0) return 0;
     if (wid == 0)
         n = snprintf(buf, sizeof buf, " %ld", v);
     else