Package: nsca Version: 2.9.1-2 Severity: important Tags: patch upstream Forwarded: http://tracker.nagios.org/view.php?id=296
I haven't heard back from upstream, so I hope this can at least be fixed in the Debian package. Also <https://dev.icinga.org/issues/2400>. > There is a race condition in src/nsca.c:open_command_file between > stat() and fopen(). If the command file exists at the point of > stat(), but is then removed before fopen() (typically by the Nagios > init script), nsca will create an empty file instead of a named pipe. > In our case, this would happen at least once every 2-3 reboots. > > It would be safer to open() the file atomically, without the O_CREAT > that is implicit in fopen(..., "w"). Other parts of the code already use > open() and related headers, so this shouldn't affect portability. > > Patch attached. > To expand a bit, here's the scenario step by step: > > 1. src/nsca.c:open_command_file checks whether the named pipe exists, > sees that it does, and decides not to use the alternate dump file: > > /* command file doesn't exist - monitoring app probably isn't running... > */ > if(stat(command_file,&statbuf)){ > > 2. An instant later, "/etc/init.d/nagios3 start" removes the pipe: > > rm -f $NagiosCommandFile ("rm -f $nagiospipe" in the Debian version.) > 3. src/nsca.c:open_command_file opens the named pipe with fopen(3), > either in "a" or "w" mode. Both of these imply open(2) with > O_CREAT, so the command file gets created as an ordinary file: > > command_file_fp=fopen(command_file,(append_to_file==TRUE)?"a":"w"); > > 4. Nagios calls mkfifo(3) from base/utils.c:open_command_file, gets > EEXIST because the path already exists, and dies ignominiously. > > If we make the first step atomic -- either an existing pipe is opened > or not -- the problem goes away. Thanks, Matej
#! /bin/sh /usr/share/dpatch/dpatch-run ## 05_command_file_race.dpatch by Matej Vela <v...@debian.org> ## ## DP: Fix race condition when opening /var/lib/nagios3/rw/nagios.cmd. @DPATCH@ diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' nsca-2.9.1~/src/nsca.c nsca-2.9.1/src/nsca.c --- nsca-2.9.1~/src/nsca.c +++ nsca-2.9.1/src/nsca.c @@ -1302,14 +1302,21 @@ /* opens the command file for writing */ static int open_command_file(void){ - struct stat statbuf; + int fd; /* file is already open */ if(command_file_fp!=NULL && using_alternate_dump_file==FALSE) return OK; + /* open the command file for writing or appending (without using + * O_CREAT like fopen() would) + */ + do + fd=open(command_file,O_WRONLY|((append_to_file==TRUE)?O_APPEND:0)); + while(fd<0 && errno==EINTR); + /* command file doesn't exist - monitoring app probably isn't running... */ - if(stat(command_file,&statbuf)){ + if(fd<0 && errno==ENOENT){ if(debug==TRUE) syslog(LOG_ERR,"Command file '%s' does not exist, attempting to use alternate dump file '%s' for output",command_file,alternate_dump_file); @@ -1326,9 +1333,7 @@ return OK; } - /* open the command file for writing or appending */ - command_file_fp=fopen(command_file,(append_to_file==TRUE)?"a":"w"); - if(command_file_fp==NULL){ + if(fd<0 || (command_file_fp=fdopen(fd,(append_to_file==TRUE)?"a":"w"))==NULL){ if(debug==TRUE) syslog(LOG_ERR,"Could not open command file '%s' for %s",command_file,(append_to_file==TRUE)?"appending":"writing"); return ERROR;