Signal handling details can be a bit complex.
Detect where signals are using signal handlers that violate
a rule about what function calls are allowed.  Assume that only a given
set of functions can be called by a signal handler and that only
sig_atomic_t data can be set.  Thus side-effects are allowed
but only through sig_atomic_t variables.  And functions can
be called, but only those from a specific set.


Example (from Robert):

char *err_msg; 
volatile sig_atomic_t e_flag = 0;
 
void handler(int signum) { 
  signal(signum, handler);
  e_flag = 1;
} 
 
int main(void) { 
  signal(SIGINT, handler); 

  err_msg = malloc(24);
  if (err_msg == NULL) {
    /* handle error condition */
  }

  strcpy(err_msg, "No errors yet.");
 
  /* main code loop */
  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }

  return 0;
}

To write a checker for such a rule as you propose, first let me define 
the rule simply:

RULE: A signal handler may only call functions on a specified list of
      async-safe functions and may modify only sig_atomic_t typed variables.

1) Assume an initial list of async-safe functions.

2) In the first iteration of how to write this checker I assume that all
   application defined functions which only calls functions in the specified 
   list and/or modifies only sig_atomic_t variables has been added to 
   the initial async-safe function list. I argue that for an initial list of async-safe 
   functions, an analysis could be done to add any application functions that 
   satisfy the async-safe property. I think this is a simple analysis and if you 
   like I can outline that analysis separately. This would handle the interprocedual
   case of calling a function in a signal handler that only called async-safe functions.

3) Use a traversal of the AST to identify function calls to the signal function "signal()"

4) At each function call to "signal()" get the parameter list and grab the second
   parameter (an object of type SgFunctionRefExp).  If you want to make sure that this
   is not an overloaded function then the function type signature can be evaluated
   and/or the location of the declaration of the function can be verified to be
   from the correct file (since this is not a link-time analysis we can't test
   anything about the library implementation).  If there exist a definition for
   "signal()" in the application then this would be suspicious, since it should be
   in a library.

5) From the SgFunctionRefExp, get the symbol (call SgFunctionRefExp::get_symbol()) which 
   will return a SgFunctionSymbol.  From the symbol call
   SgFunctionSymbol::get_declaration() to get the SgFunctionDeclaration object. From the
   funtion declaration, get the SgFunctionDefinition object.

6) Do a nested query on the SgFunctionDefinition object to get the list of functions
   that are called.  Verify that each function being called is in our list of async-safe 
   function.  To avoid repeatly reviewing this function the result of the first
   test of this function could store an attribute (see AST Attributes in the ROSE Tutorial).

7) Do a nested query (can be combined with the one in step #6) to identify all
   SgVariableRefExp object.  Verify that for each SgVariableRefExp object which is
   an lvalue (call the SgExpression::islvalue() member function), that the type
   is sig_atomic_t or can be stripped away to a type for which sig_atomic_t is the
   base type (this handles the case of layers of typedefs hiding the fact that
   a variable may really be of sig_atomic_t, but just not obviously so). The 
   result from this analysis could be stored in the same attribute as in step #6.

8) If steps #6 or #7 detect a violation, then report a violation using the 
   ouput object in Compasswith a string the describes the flavor of the violation
   (either an inappropriate function call (not from our list of async-safe functions)
    or modification of a variable that was not sig_atomic_t).

I would separate out step #2 into its own traversal of the AST, and then combine
steps #6 and #7 as a second traversal.  Alternatively, techniques in ROSE could do
this with out a traversal of the whole AST, but I am trying to describe a simple
implementation. The only advantage of alternative techniques would be
performance, but the traversal of a 250K line code is only about a second, so this
is not a big deal in this case.  It is already the case that separate traversals
can be combined so that where compass will do hundreds of checkers (eventually, we hope)
they are all combined in the single traversal for a performance improvement currently 
at 110X speedup.  Parallelization over the checkers has shown excellent performance 
improvements as well, but this is newer work.

Let me know if you would like me to implement this as a checker in Compass, now that
you have shown me some of the details of signal semantics it is more clear that
there should be a lot of checkers that report on many of the other subtle details 
of signal handling.  This is a excellent example of how a library adds semantic
constrants that the base language could not hope to enforce and so other mechanisms
should wrap a vendor compiler to provide such domain-specific safety or security
analysis support.

I have attached a dot graph of your example program, plus a pdf version.
both show the internal AST. 

Thanks,
Dan


I have included the program below that I used to generate the 
certSignalExample.C.pdf
certSignalExample.C.dot
certSignalExample_WholeAST.dot
graphs, in case you want to look at them to see how the AST is organized
(the certSignalExample_WholeAST.dot, shows a LOT more detail). Where
as the first two show what we consider to define the AST (without types
and other details which can be consider attributes to the AST).

Note that the certSignalExample.C.pdf and certSignalExample.C.dot 
don't show the function parameters, but they are available just not
in the in the graphs (to simplify the definition of the AST we
don't include function parameters in the traversal).  So at the
function declaration you have to call a function to get the list
of parameters).

#if 0
// The certSignalExample.C.pdf and certSignalExample.C.dot graphs
// are build using the header files below.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#else
// I have included just the relavant declarations required to compile
// the program to reduce the size and complexity of the whole program graph
// for the certSignalExample_WholeAST.dot graph.
typedef void (*functionType)(int x);
void signal(int s, functionType f);
typedef long sig_atomic_t;
void strcpy(char* s1, char* s2);
const int SIGINT = 42;
const void* NULL = 0L;
void* malloc(int size);
#endif

char *err_msg; 
volatile sig_atomic_t e_flag = 0;
 
void handler(int signum) { 
  signal(signum, handler);
  e_flag = 1;
} 
 
int main(void) { 
  signal(SIGINT, handler); 

  err_msg = (char*) malloc(24);
  if (err_msg == NULL) {
    /* handle error condition */
  }

  strcpy(err_msg, "No errors yet.");
 
  /* main code loop */
  if (e_flag) {
    strcpy(err_msg, "SIGINT received.");
  }

  return 0;
}




The following table defines a set of functions that shall be either reentrant or non-interruptible by signals and shall be async-signal-safe. Therefore applications may invoke them, without restriction, from signal-catching functions:


_Exit()
_exit()
abort()
accept()
access()
aio_error()
aio_return()
aio_suspend()
alarm()
bind()
cfgetispeed()
cfgetospeed()
cfsetispeed()
cfsetospeed()
chdir()
chmod()
chown()
clock_gettime()
close()
connect()
creat()
dup()
dup2()
execle()
execve()
fchmod()
fchown()
fcntl()
fdatasync()
fork()
 
	


fpathconf()
fstat()
fsync()
ftruncate()
getegid()
geteuid()
getgid()
getgroups()
getpeername()
getpgrp()
getpid()
getppid()
getsockname()
getsockopt()
getuid()
kill()
link()
listen()
lseek()
lstat()
mkdir()
mkfifo()
open()
pathconf()
pause()
pipe()
poll()
posix_trace_event()
pselect()
raise()
 
	


read()
readlink()
recv()
recvfrom()
recvmsg()
rename()
rmdir()
select()
sem_post()
send()
sendmsg()
sendto()
setgid()
setpgid()
setsid()
setsockopt()
setuid()
shutdown()
sigaction()
sigaddset()
sigdelset()
sigemptyset()
sigfillset()
sigismember()
sleep()
signal()
sigpause()
sigpending()
sigprocmask()
sigqueue()
 
	


sigset()
sigsuspend()
sockatmark()
socket()
socketpair()
stat()
symlink()
sysconf()
tcdrain()
tcflow()
tcflush()
tcgetattr()
tcgetpgrp()
tcsendbreak()
tcsetattr()
tcsetpgrp()
time()
timer_getoverrun()
timer_gettime()
timer_settime()
times()
umask()
uname()
unlink()
utime()
wait()
waitpid()
write()
 

All functions not in the above table are considered to be unsafe with respect to signals. In the presence of signals, all functions defined by this volume of IEEE Std 1003.1-2001 shall behave as defined when called from or interrupted by a signal-catching function, with a single exception: when a signal interrupts an unsafe function and the signal-catching function calls an unsafe function, the behavior is undefined.



Steps:

1) Run the gen_checker.sh script to generate the source code templates:

tux243.llnl.gov{dquinlan}87: ~/COMPASS_SUBMIT/bin/gen_checker.sh asynchronous signal handler
******************************************************************************
Created the directory asynchronousSignalHandler.
Copied the files "compass.C compass.h compassTestMain.C compass_parameters"
into the directory asynchronousSignalHandler.

Generated asynchronousSignalHandler/Makefile
Generated asynchronousSignalHandler/Makefile.am
Generated asynchronousSignalHandler/asynchronousSignalHandler.h
Generated asynchronousSignalHandler/asynchronousSignalHandlerMain.C
Generated asynchronousSignalHandler/asynchronousSignalHandlerTest1.C
Generated asynchronousSignalHandler/../asynchronousSignalHandler/asynchronousSignalHandlerDocs.tex
******************************************************************************
All files have been generated successfully. You should modify at least the
variable ${ROSE_INSTALL} in asynchronousSignalHandler/Makefile to get a
working system. Good luck with your style-checker!
tux243.llnl.gov{dquinlan}88:
