Nmap Development mailing list archives

Re: Nmap doesn't register signal handlers


From: doug () hcsw org
Date: Thu, 13 Aug 2009 01:13:02 +0000

Alexander makes some great points.

On Wed, Aug 12, 2009 at 11:33:02PM +0400 or thereabouts, Solar Designer wrote:
This call, as well as most or even all other calls made from this signal
handler, are unsafe.  There are only a handful of functions that may be
safely used in a signal handler; most are unsafe,  Any/all use of stdio
is unsafe.

malloc() and free() are also unsafe to use in signal handlers. Basically
unless an API specifically says it's re-entrant you should assume that it
is not safe to use in signal handlers.

  log_close(LOG_MACHINE|LOG_NORMAL|LOG_SKID);

Likely unsafe (I did not check that function).

This function calls fclose() so it cannot be used in signal handlers.

  if (abt) abort();

abort() flushes and closes stdio streams, hence it is unsafe.

  exit(1);

Same as above, plus invokes atexit handlers, hence unsafe.

Yup and also see destructors:

http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Sure, a lot of programs do stuff like this (and segfault on it once in a
while), but if you're working on this code currently it may be a good
time to fix it.  Given the purpose of the signal handler above, a proper
fix, while preserving the functionality, is non-trivial.  Maybe you'll
have to sacrifice some of the functionality.

I agree. Even though with a program like nmap I think the risks are fairly
low, nmap should still never re-enter stdio routines as will be done if these
signal handlers are re-enabled.

Another idea is to use write(2) (with a loop around it) and _exit(2) -
just the direct syscalls - to print an error message and terminate the
program right from the signal handler.

I think for nmap this is the best solution. Some already buffered messages
may be lost but this is preferable to re-entering stdio routines.

An even better idea is to use a combination of both approaches - merely
set a flag on the first occurrence of a signal (and let the main program
handle it), but if the signal arrives again (perhaps because the main
program failed to handle the flag in time), resort to write/_exit.  This
is what JtR does in sig_handle_abort().

For those that don't know, this works because system calls that are
interrupted by signals will return EINTR specifically to support
the technique Alexander described.

However, if your signal handlers were installed with SA_RESTART (see
sigaction(2)), system calls will be auto-restarted and will not return
EINTR. In this case, before you enter your main loop you should open a
pipe. In the signal handler write(2) a single byte to the pipe. When
you see this byte on the other end of the pipe in your main loop, run
whatever code should be done to handle the signal.

The thing with nmap is that there are several main loops (ultra_scan,
nsock, etc). So to perform re-entrant operations on signals, each one
of these main loops must be modified either to handle EINTR or to listen
on a pipe for a signaller byte.

Finally, I recommend against trying to catch SIGSEGV and act on it.

Fortunately I don't think there is any reason for nmap to ever do this.

Thanks for writing such a detailed explanation, Alexander.

Regards,

Doug

PS. Calling exec(2) in signal handlers is almost definitely not a
good idea (before or after a fork()). Doing so will introduce
file-descriptor leakage even if you are careful to apply CLOEXEC
to all descriptors. Consider this sequence of execution:

fd = open_some_descriptor();
     // signal happens right here and handler exec()s
fcntl(fd, F_SETFD, FD_CLOEXEC);

There were some recentish patches to linux to allow you to open and
CLOEXEC descriptors in single operations but afaik there really
isn't a good general solution to this issue so just don't fork
in signal handlers:

http://udrepper.livejournal.com/20407.html

Attachment: _bin
Description:


_______________________________________________
Sent through the nmap-dev mailing list
http://cgi.insecure.org/mailman/listinfo/nmap-dev
Archived at http://SecLists.Org

Current thread: