[tex-k] Uninterruptible loop in web2c TeX

Karl Berry karl at freefriends.org
Fri Aug 28 22:54:31 CEST 2020


    1) run `tex'
    2) type `\relax' and <return>
    3) press Ctrl+C
    4) press Ctrl+D

    On my system I get uninterruptible endless loop which prints:

        *(Please type a command or say `\end'.)

It happens with certain versions of glibc, notably 2.28-10 (used on
Debian buster), which apparently do not clear errno with getc (and this
is allowed; I'm not sure why they made the behavior different
elsewhere).

Thus errno is set to EINTR by the CTRL-C and then it persists in being
errno, and the code has to ignore that case in general (the read system
call can be interrupted and it's our job to restart it). Endless loop
results.

So our code has to distinguish a real EOF (from the CTRL-D) from the
"fake" EOF with errno==EINTR, by explicitly clearing errno before
calling getc. Again, I'm surprised this was not necessary earlier.

By the way, the same culprit caused an infloop in an even simpler case:
type CTRL-C and then CTRL-D to the ** prompt.

My fix was to explicitly clear errno and then loop as long as EINTR is
(newly) set from a getc call. Here is the change I just committed to TL
(r56202). Hope it doesn't cause new trouble.

Thanks for the report, and to Paul V and Norbert for helping debug. -k

--- texmfmp.c	(revision 56198)
+++ texmfmp.c	(working copy)
@@ -2498,13 +2498,33 @@ input_line (FILE *f)
       }
     }
   }
-#endif
+#endif /* WIN32 */
   last = first;
-  while (last < bufsize && (i = getc (f)) != EOF && i != '\n' && i != '\r')
-    buffer[last++] = i;
-#endif
+  do {
+    errno = 0; /* otherwise EINTR might wrongly persist */
+    while (last < bufsize && (i = getc (f)) != EOF && i != '\n' && i != '\r')
+      buffer[last++] = i;
 
-  if (i == EOF && errno != EINTR && last == first)
+    /* The story on EINTR: because we tell libc to pass interrupts
+       through (see SA_INTERRUPT above), we have to make sure that we
+       check for and ignore EINTR when getc reads an EOF; hence the
+       outer do..while loop here (and a similar loop below).
+       
+       On the other hand, we have to make sure that we detect a real
+       EOF. Otherwise, for example, typing CTRL-C and then CTRL-D to the
+       ** prompt results in an infinite loop, because we
+       (input_line) would never return false. On glibc 2.28-10 (Debian
+       10/buster), and probably other versions, errno is evidently not
+       cleared as a side effect of getc (and this is allowed).
+       Therefore we clear errno before calling getc above.
+       
+       Original report (thread following has many irrelevant diversions):
+       https://tug.org/pipermail/tex-k/2020-August/003297.html  */
+
+  } while (i == EOF && errno == EINTR);
+#endif /* not IS_pTeX */
+
+  if (i == EOF && last == first)
     return false;
 
   /* We didn't get the whole line because our buffer was too small.  */


More information about the tex-k mailing list.