[comp.os.minix] Signal bugs

brucee@runx.ips.oz (Bruce Evans) (09/27/88)

While testing some shell modifications recently, I ran into and fixed
some bugs in signal handling. Try running
   while :; echo ok; done
and interrupting it with a SIGINT. It will probably hang within 10 tries.
The F1 dump will probably show the shell stuck with flag 10 (signal
PENDING). Another SIGINT may free the shell.

Due to another bug in my own kernel changes, the signal bug crashed the
system completely, so I had to fix everything. (Mine was readying a
process with signals pending).

(1) In lib/signal.c, there is a race which can lead to the signal catcher
calling location 0 or 1 corresponding to SIG_DFL or SIG_IGN. This is
because the "address" of the new signal handler is installed in vectab[]
too early, so switching from a catcher to a non-function may cause
trouble. The solution is to keep the old address longer. There is also
a problem switching in the opposite direction, solved by installing the
catcher early. It is possible to avoid the system call completely when
switching from a catcher to another catcher, but I didn't try this since
it looked like the code would be larger.

I think this bug may be hard to duplicate but it was reliable in combination
with my local bug.

(2) In kernel/system.c, the (new in 1.3) signal PENDING flag is not managed
properly. In the example, MM spends a lot of time forking and a signal in
the middle can lead to the PENDING flag being copied. This messes up the
count of PENDING flags in sig_procs, at least. It turns out to be correct
to avoid copying the flag, since bumping sig_procs gives an extra signal
after the first one is propagated through the process group.

A quick look at other assignments of the flags showed the one in do_xit()
which may invalidate sig_procs. MM could be just about to call sys_exit()
when a signal arrives, so the test is necessary.

(3) This is not quite a bug. In kernel/proc.c/mini_rec(), after inform()
is called, the current process remains active, even though MM has just
been readied and a user process is running. This violates the priorities
and delays the signal handling unnecessarily. I just call pick_proc().
Perhaps pick_proc() should be called at the end of sendrec() instead of
after unready(), so it picks up high priority processes activated by
ready() without being called twice.

#! /bin/sh
# Contents:  proc.c.cdif signal.c.cdif system.c.cdif
# Wrapped by sys@besplex on Sun Sep 25 21:23:52 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'proc.c.cdif' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'proc.c.cdif'\"
else
echo shar: Extracting \"'proc.c.cdif'\" \(688 characters\)
sed "s/^X//" >'proc.c.cdif' <<'END_OF_FILE'
X*** /user/sys/kernel/proc.c	Wed Aug  3 21:18:45 1988
X--- proc.c	Sun Sep 25 21:00:15 1988
X***************
X*** 241,247 ****
X    /* If MM has just blocked and there are kernel signals pending, now is the
X     * time to tell MM about them, since it will be able to accept the message.
X     */
X!   if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) inform();
X    return(OK);
X  }
X  
X--- 241,250 ----
X    /* If MM has just blocked and there are kernel signals pending, now is the
X     * time to tell MM about them, since it will be able to accept the message.
X     */
X!   if (sig_procs > 0 && caller == MM_PROC_NR && src == ANY) {
X! 	inform();
X! 	pick_proc();
X!   }
X    return(OK);
X  }
X  
END_OF_FILE
if test 688 -ne `wc -c <'proc.c.cdif'`; then
    echo shar: \"'proc.c.cdif'\" unpacked with wrong size!
fi
# end of 'proc.c.cdif'
fi
if test -f 'signal.c.cdif' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'signal.c.cdif'\"
else
echo shar: Extracting \"'signal.c.cdif'\" \(1018 characters\)
sed "s/^X//" >'signal.c.cdif' <<'END_OF_FILE'
X*** /user/sys/lib/signal.c	Wed Aug  3 21:37:26 1988
X--- signal.c	Sun Sep 25 20:42:36 1988
X***************
X*** 19,29 ****
X    int r,(*old)();
X  
X    old = vectab[signr - 1];
X!   vectab[signr - 1] = func;
X!   M.m6_i1 = signr;
X!   M.m6_f1 = ( (func == SIG_IGN || func == SIG_DFL) ? func : begsig);
X!   r = callx(MM, SIGNAL);
X!   if (r == 1)
X! 	old = SIG_IGN;
X!   return( (r < 0 ? (int (*)()) r : old) );
X! }
X--- 19,40 ----
X    int r,(*old)();
X  
X    old = vectab[signr - 1];
X!   M.m6_i1 = signr;
X!   if (func == SIG_IGN || func == SIG_DFL)
X! 	/* keep old signal catcher until it is completely de-installed */
X! 	M.m6_f1 = func;
X!   else {
X! 	/* use new signal catcher immediately (old one may not exist) */
X! 	vectab[signr - 1] = func;
X! 	M.m6_f1 = begsig;
X!   }
X!   r = callx(MM, SIGNAL);
X!   if (r < 0) {
X! 	vectab[signr - 1] = old;	/* undo any pre-installation */
X! 	return( (int (*)()) r );
X!   }
X!   vectab[signr - 1] = func;		/* redo any pre-installation */
X!   if (r == 1)
X! 	return(SIG_IGN);
X!   return(old);
X! }
END_OF_FILE
if test 1018 -ne `wc -c <'signal.c.cdif'`; then
    echo shar: \"'signal.c.cdif'\" unpacked with wrong size!
fi
# end of 'signal.c.cdif'
fi
if test -f 'system.c.cdif' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'system.c.cdif'\"
else
echo shar: Extracting \"'system.c.cdif'\" \(670 characters\)
sed "s/^X//" >'system.c.cdif' <<'END_OF_FILE'
X*** /user/sys/kernel/system.c	Wed Aug  3 21:18:46 1988
X--- system.c	Sun Sep 25 21:11:27 1988
X***************
X*** 136,141 ****
X--- 136,143 ----
X    while (bytes--) *dptr++ = *sptr++;	/* copy parent struct to child */
X  
X    rpc->p_flags |= NO_MAP;	/* inhibit the process from running */
X+   rp->p_flags &= ~PENDING;	/* only one in group should have PENDING */
X+   rp->p_pending = 0;
X    rpc->p_pid = pid;		/* install child's pid */
X    rpc->p_reg[RET_REG] = 0;	/* child sees pid = 0 to know it is child */
X  
X***************
X*** 271,276 ****
X--- 273,279 ----
X  		}
X  	}
X    }
X+   if (rc->p_flags & PENDING) --sig_procs;
X    rc->p_flags = P_SLOT_FREE;
X    return(OK);
X  }
END_OF_FILE
if test 670 -ne `wc -c <'system.c.cdif'`; then
    echo shar: \"'system.c.cdif'\" unpacked with wrong size!
fi
# end of 'system.c.cdif'
fi
echo shar: End of shell archive.
exit 0

Bruce Evans
Internet: brucee@runx.ips.oz.au    UUCP: uunet!runx.ips.oz.au!brucee