Eric Blake wrote on 2011-02-10:
> POSIX requires that this program have an identical first and last line:
> #include <stdio.h>
> #include <string.h>
> #include <errno.h>
> int main (void) {
>   char *err = strerror(1000);
>   printf ("%s\n", err);
>   errno = 2000;
>   perror ("hi");
>   printf ("%s\n", err);
>   return 0;
> }
> but on cygwin 1.7.7, the perror() corrupts the buffer returned by
> strerror().  We should probably fix that in gnulib as part of our perror
> module.

Yes, but first let's see which other problems there are.

The program below tests whether strerror's buffer is read-write (i.e.
whether it is overwritten by subsequent strerror calls) and, when compiled
with -DPERROR, whether perror calls clobber the strerror buffer.

The result is:

           read-write buffer?    perror reuses strerror buffer?
glibc           yes                   no
OpenBSD         yes                   no
OSF/1           yes                   no
Cygwin 1.5      yes                   yes
Cygwin 1.7      yes                   yes
mingw           yes                   no

That's the situation without gnulib. With gnulib, there are three problems
in toto:
  1) The strerror_r replacement, when EXTEND_STRERROR_R is defined,
     clobbers the strerror function's buffer, which it shouldn't.
  2) The perror replacement uses strerror, thus clobbering the strerror
  3) On Cygwin, perror clobbers the strerror buffer.

The fix for 1) should be to move most of lib/strerror.c to lib/strerror_r.c.
The fix for 2) should be to change lib/perror.c to call strerror_r.
The fix for 3) should be to change m4/perror.m4 to enable the replacement
on Cygwin.

I think the three fixes should be applied in this order, bottom-up.


==================================== foo.c ====================================
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main ()
  const char *msg1;
  const char *msg2;
  const char *msg3;

  msg1 = strerror (ENOENT);
  printf ("msg1 before: %s\n", msg1);
  msg2 = strerror (-4);
  printf ("msg2 before: %s\n", msg2);
  msg3 = strerror (1729576);
  printf ("msg3 before: %s\n", msg3);

  freopen ("/dev/null", "w", stderr);

#ifdef PERROR
  errno = EACCES; perror ("");
  errno = -5; perror ("");
  errno = 153272; perror ("");
  strerror (EACCES);
  strerror (-5);
  strerror (153272);

  printf ("msg1 after:  %s\n", msg1);
  printf ("msg2 after:  %s\n", msg2);
  printf ("msg3 after:  %s\n", msg3);

  return 0;

Reply via email to