[net.unix-wizards] The errno variable can get trashed

LeFebvre.wbst@XEROX.ARPA (08/06/84)

> The solution would seem to be to have the C signal handling mechanism
> save errno before calling the signal routine and restore it after
> the routine has returned.

Alternatively (actually, in lieu of), the signal handling routine itself
could save and restore the value of errno.  It is possible to assign errno
a value explicitly.  The routine "i" in your example could be written:

void i()
{ int olderrno;	/* note the name is exactly 8 characters :-) */
  olderrno = errno;
  signal(SIGALRM,i);
  open(".",1); /* Fails, since is a directory */
  errno = olderrno;
}

Of course, you also have to move the definition of errno to before the
"void i" statement.

	William LeFebvre
	Department of Computer Science
	Rice University
	At Xerox for the summer (but not much longer)

ka@hou3c.UUCP (Kenneth Almquist) (08/06/84)

	The global 'errno' variable set by the assembler routines in the
	C library for system-call interface can be garbled before the
	program gets to look at it if a signal arrives and the signal-handling
	routine does another system call (which gets an error).

This is a specific case of a general problem with signal handlers that
modify global or static variables.  For example, a call to malloc in a
signal handler can result in a corrupted free space list.  Radford suggests
that the signal mechanism should save and restore the value of errno, but
the only solution to the general problem is care on the part of the
programmer.

One way to fix Radford's example is to have the alarm routine simply
set a flag for later processing.  The code becomes:

	static int alflag;	/* set when alarm signal received */

	void i() {		/* called on alarm signal */
		alflag++;
	}

	void testalarm() {	/* called from loop in main routine */
		if (alflag) {
			alflag = 0;
			signal(SIGALARM, i);
			alarm(1);
			open(".", 1);		/* this will fail */
		}
	}

Another fix would of course be to have the routine "i" save and restore
the value of errno.
				Kenneth Almquist

rml@hpfcls.UUCP (rml) (08/15/84)

> The solution would seem to be to have the C signal handling mechanism
> save errno before calling the signal routine and restore it after
> the routine has returned.

This is impossible with the standard implementation of the system call
interface and errno.  Errno is is an arbitrary location in the user's
address space which is unknown to the kernel.  That's why the kernel passes
the errno value back in a register and an assembly language library routine
copies the value to the global variable.

If this problem is a concern for you, you can simulate your suggested
solution in your own signal handler:

void i()
{ int save_errno;
  extern int errno;

  save_errno = errno;
  signal(SIGALRM,i);
  alarm(1);
  open(".",1); /* Fails, since is a directory */
  errno = save_errno;
}

> Is the fact that this bug has been present for upwards of ten years
> in all versions of Unix, apparently without anyone noticing, an
> indication of how often Unix programmers check for errors?

The fact that you can cause this behavior about once a minute with a
contrived example does not indicate that this is a serious problem.
Admittedly, many programs running on UNIX* systems (including a number
of commands shipped with UNIX systems) are lax about checking for errors
in system calls or about reporting the reason for the error when they do
check.  However, the fact that a simple workaround exists for those
programmers who are concerned is a point in favor of UNIX systems.

				Bob Lenk
				{hplabs, ihnp4}!hpfcla!rml

*UNIX is a trademark of AT&T Bell Laboratories