[splint-discuss] Problem in using WIFSIGNALED(status)

Richard A. O'Keefe ok at cs.otago.ac.nz
Sun Mar 30 17:57:52 PST 2008


On 28 Mar 2008, at 5:34 pm, Vishal Bayskar wrote:
> The definition of macro WIFSIGNALED is given in the /usr/include/sys/ 
> wait.h
> As
> # define WIFSIGNALED(status)    __WIFSIGNALED(__WAIT_INT(status))
>
>
> And the definition of macro  __WIFSIGNALED is given in /usr/include/ 
> bits/waitstatus.h
>             As
>                         #define __WIFSIGNALED(status) \
>   (((signed char) (((status) & 0x7f) + 1) >> 1) > 0)

Let's analyse this.
(status) & 0x7f can be 0..127
((status) & 0x7f) + 1 can be 1..128
(signed char)(((status) & 0x7f) + 1) can be 1..127 or -128
(signed char)(((status) & 0x7f) + 1) >> 1 can be 0..63 or (-1 or 64)
   where -1 is chosen if (signed char)x >> 1 uses a signed right shift
     and 64 is chosen if (signed char)x >> 1 uses an unsigned right  
shift.
Why would anyone use unsigned right shifts for signed integers of any  
size?
I have no idea, perhaps because the hardware doesn't have a suitable  
shift
and speed was deemed more important than usefulness, but the C standard
says of E1 >> E2 that
	If E1 has a signed type and a negative value,
	the resulting value is IMPLEMENTATION-DEFINED.
So in fact when I said that the possible outcome of (signed char)(127  
+ 1)>>1
was either -1 or 64, I was hopelessly optimistic.  It could be  
ANYTHING.  But
64 is enough to get us into trouble, and there are C compilers that do  
use
unsigned right shifts even on signed numbers.

I note that Solaris and MacOS X use different definitions for
WIFSIGNALED which are completely free of this problem.

So splint is *correctly* warning you that the macro is not portable
between C compilers.

Does this actually matter?  After all, the bottom 7 bits of the status
here are the signal number of the uncaught signal that killed the  
program.
In Solaris and MacOS X, a program that has been stopped has the STOP
signal number there, so they never ever have a 127 in that position,
though they wouldn't have a problem if they did have one.  But Linux
puts 127 there to indicate a stopped program, so the intention is
	- reject 0 (a program that exited normally)
	- reject 127 (a program paused by SIGSTOP)
	- accept everything else
and what happens to 127 matters very much.

Whoever wrote that test clearly expected a signed right shift, and  
that's
what gcc will give you.  But there is no good reason to expect any other
compiler to do the same.  So splint has actually pointed out a a REAL
portability issue:  whether stopped programs are accepted by WIFSIGNALED
depends on your compiler.

The simplest compiler-portable definition would just be

	#define __WIFSIGNALED(status)             ( \
	    ((unsigned)(status) & 0x7fu) != 0 &&    \
	    ((unsigned)(status) & 0x7fu) != 0x7fu )

which is basically the approach that Solaris and MacOS X take.
The wait macros are explicitly described as macros and may evaluate  
their
arguments as often as they wish.

>

> Please help me in resolving the problem, how could I overcome of  
> this warning, without using –type flag with splint command

The best way I can think of is to avoid WIFSIGNALED entirely
and do
	if (WIFSTOPPED(status)) {
	    /* it isn't actually completed, just paused */
	} else
	if (WIFEXITED(status)) {
	   /* it exited normally */
	} else {
	   /* it was killed by an uncaught signal */
	}

Next simplest,

	#include <sys/wait.h>
	#undef WIFSIGNALED
	#define WIFSIGNALED(x) (!WIFEXITED(x) && !WIFSTOPPED(x))

which should work under Solaris and MacOS X as well.


More information about the splint-discuss mailing list