1    	/****************************************************************************
2    	 *   Copyright (C) 2006-2008 by Jason Ansel                                 *
3    	 *   jansel@csail.mit.edu                                                   *
4    	 *                                                                          *
5    	 *   This file is part of the JALIB module of DMTCP (DMTCP:dmtcp/jalib).    *
6    	 *                                                                          *
7    	 *  DMTCP:dmtcp/jalib is free software: you can redistribute it and/or      *
8    	 *  modify it under the terms of the GNU Lesser General Public License as   *
9    	 *  published by the Free Software Foundation, either version 3 of the      *
10   	 *  License, or (at your option) any later version.                         *
11   	 *                                                                          *
12   	 *  DMTCP:dmtcp/src is distributed in the hope that it will be useful,      *
13   	 *  but WITHOUT ANY WARRANTY; without even the implied warranty of          *
14   	 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
15   	 *  GNU Lesser General Public License for more details.                     *
16   	 *                                                                          *
17   	 *  You should have received a copy of the GNU Lesser General Public        *
18   	 *  License along with DMTCP:dmtcp/src.  If not, see                        *
19   	 *  <http://www.gnu.org/licenses/>.                                         *
20   	 ****************************************************************************/
21   	
22   	#include "jassert.h"
23   	#include "jfilesystem.h"
24   	#include <sys/types.h>
25   	#include <unistd.h>
26   	#include "jconvert.h"
27   	#include <sys/types.h>
28   	#include <sys/stat.h>
29   	#include <fcntl.h>
30   	#include <dlfcn.h>
31   	
32   	#include <fstream>
33   	#include <execinfo.h>  /* For backtrace() */
34   	
35   	// Needed for dmtcp::UniquePid::getTmpDir()
36   	// Is there a cleaner way to get information from rest of DMTCP?
37   	#include "../src/uniquepid.h"
38   	#include "../src/util.h"
39   	#include "../src/protectedfds.h"
40   	
41   	#undef JASSERT_CONT_A
42   	#undef JASSERT_CONT_B
43   	
44   	// This macro is also defined in ../src/constants.h and should always be kept
45   	// in sync with that.
46   	#define LIBC_FILENAME "libc.so.6"
47   	
48   	#ifndef DMTCP
49   	#  define DECORATE_FN(fn) ::fn
50   	#else
51   	#  include "syscallwrappers.h"
52   	#  define DECORATE_FN(fn) ::_real_ ## fn
53   	#endif
54   	
55   	int jassert_quiet = 0;
56   	
57   	#define DUP_STDERR_FD PROTECTED_STDERR_FD
58   	#define DUP_LOG_FD    PROTECTED_JASSERTLOG_FD
59   	
60   	static int jwrite(int fd, const char *str)
61   	{
62   	  return dmtcp::Util::writeAll(fd, str, strlen(str));
63   	#if 0
64   	  ssize_t offs, rc;
65   	  ssize_t size = strlen(str);
66   	
67   	  for (offs = 0; offs < size;) {
68   	    rc = write (fd, str + offs, size - offs);
69   	    if (rc == -1 && errno != EINTR && errno != EAGAIN) 
70   	      return rc;
71   	    else if (rc > 0)
72   	      offs += rc;
73   	  }
74   	  return size;
75   	#endif
76   	}
77   	
78   	int jassert_internal::jassert_console_fd()
79   	{
80   	  //make sure stream is open
81   	  jassert_safe_print ( "" );
82   	  return DUP_STDERR_FD;
83   	}
84   	
85   	jassert_internal::JAssert& jassert_internal::JAssert::Text ( const char* msg )
86   	{
87   	  Print ( "Message: " );
88   	  Print ( msg );
89   	  Print ( "\n" );
90   	  return *this;
91   	}
92   	
93   	static pthread_mutex_t logLock = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
94   	
95   	bool jassert_internal::lockLog()
96   	{
97   	  int retVal = pthread_mutex_lock(&logLock);
98   	  if (retVal != 0) {
99   	    perror ( "jassert_internal::lockLog: Error acquiring mutex");
100  	  }
101  	  return retVal == 0;
102  	}
103  	
104  	void jassert_internal::unlockLog()
105  	{
106  	  int retVal = pthread_mutex_unlock(&logLock);
107  	  if (retVal != 0) {
108  	    perror ( "jassert_internal::unlockLog: Error releasing mutex");
109  	  }
110  	}
111  	
112  	jassert_internal::JAssert::JAssert ( bool exitWhenDone )
113  	    : JASSERT_CONT_A ( *this )
114  	    , JASSERT_CONT_B ( *this )
115  	    , _exitWhenDone ( exitWhenDone )
116  	{
117  	  _logLockAcquired = jassert_internal::lockLog();
118  	}
119  	
120  	jassert_internal::JAssert::~JAssert()
121  	{
122  	  if ( _exitWhenDone ) {
123  	    Print ( jalib::Filesystem::GetProgramName() );
124  	    Print ( " (" );
125  	    Print ( getpid() );
126  	    Print ( "): Terminating...\n" );
127  	#ifdef DEBUG
128  	    jbacktrace();
129  	#endif
130  	  }
131  	
132  	  jassert_safe_print ( ss.str().c_str() );
133  	  if ( _logLockAcquired )
134  	    jassert_internal::unlockLog();
135  	
136  	  if ( _exitWhenDone ) {
137  	    _exit ( 1 );
138  	  }
139  	}
140  	
141  	const char* jassert_internal::jassert_basename ( const char* str )
142  	{
143  	  for ( const char* c = str; c[0] != '\0' && c[1] !='\0' ; ++c ) {
144  	    if ( c[0]=='/' )
145  	      str=c+1;
146  	  }
147  	  return str;
148  	}
149  	
150  	static int _open_log_safe ( const char* filename, int protectedFd )
151  	{
152  	  //open file
Event tainted_string_sink_content_lv_call: Passing tainted string "filename" to "_real_open", which depends on its content. [details]
153  	  int tfd = _real_open ( filename, O_WRONLY | O_APPEND | O_CREAT /*| O_SYNC*/,
154  	                                   S_IRUSR | S_IWUSR );
155  	  if (tfd == -1) return -1;
156  	  //change fd to 827 (DUP_LOG_FD -- PFD(6))
157  	  int nfd = dup2 ( tfd, protectedFd );
158  	  close ( tfd );
159  	
160  	  return nfd;
161  	}
162  	
163  	static int _open_log_safe ( const jalib::string& s, int protectedFd )
164  	{
165  	  return _open_log_safe ( s.c_str(), protectedFd );
166  	}
167  	
168  	
169  	static int theLogFileFd = -1;
170  	static int errConsoleFd = -1;
171  	
172  	static jalib::string& theLogFilePath() {static jalib::string s;return s;};
173  	
174  	void jassert_internal::jassert_init ( const jalib::string& f )
175  	{
176  	#ifdef DEBUG
177  	  JASSERT_SET_LOGFILE(f);
178  	#endif
179  	  jassert_safe_print("");
180  	}
181  	
182  	const jalib::string writeJbacktraceMsg() {
183  	  jalib::string msg = jalib::string("")
184  	    + "\n   *** Stack trace is available ***\n" \
185  	    "   Execute:  utils/dmtcp_backtrace.py  [found in DMTCP_ROOT]\n" \
186  	    "   For usage:  utils/dmtcp_backtrace.py --help\n" \
187  	    "   Files saved: ";
188  	  msg += dmtcp::UniquePid::getTmpDir()
189  	                          + "/backtrace." + jalib::XToString ( getpid() );
190  	  msg += "\n                ";
191  	  msg += dmtcp::UniquePid::getTmpDir()
192  	                          + "/proc-maps." + jalib::XToString ( getpid() );
193  	  msg += "\n";
194  	  return msg;
195  	}
196  	
197  	void writeBacktrace() {
198  	  void *buffer[BT_SIZE];
199  	  int nptrs = backtrace(buffer, BT_SIZE);
200  	  jalib::string backtrace = dmtcp::UniquePid::getTmpDir()
201  	                          + "/backtrace." + jalib::XToString ( getpid() );
202  	  int fd = open(backtrace.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
203  	  if (fd != -1) {
204  	    backtrace_symbols_fd( buffer, nptrs, fd );
205  	    close(fd);
206  	    jalib::string lnk = dmtcp::UniquePid::getTmpDir() + "/backtrace";
207  	    unlink(lnk.c_str());  // just in case it had previously been created.
208  	    if (symlink(backtrace.c_str(), lnk.c_str()) == -1)
209  	      {}  // Too late to issue a user warning here.
210  	  }
211  	}
212  	
213  	// DOES:  cp /proc/self/maps $DMTCP_TMPDIR/proc-maps
214  	// But it could be dangerous to spawn a process in fragile state of JASSERT.
215  	void writeProcMaps() {
216  	  char *mapsBuf = (char*) JALLOC_HELPER_MALLOC(50000);
217  	  int rc, count, total;
218  	  int fd = open("/proc/self/maps", O_RDONLY);
219  	  if (fd == -1) return;
220  	  count = dmtcp::Util::readAll(fd, mapsBuf, sizeof(mapsBuf) - 1);
221  	  close(fd);
222  	  jalib::string procMaps = dmtcp::UniquePid::getTmpDir()
223  	                          + "/proc-maps." + jalib::XToString ( getpid() );
224  	  fd = open(procMaps.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR);
225  	  if (fd == -1) return;
226  	  count = dmtcp::Util::writeAll(fd, mapsBuf, count);
227  	  close(fd);
228  	  jalib::string lnk = dmtcp::UniquePid::getTmpDir() + "/proc-maps";
229  	  unlink(lnk.c_str());  // just in case it had previously been created.
230  	  if (symlink(procMaps.c_str(), lnk.c_str()) == -1)
231  	  {}  // Too late to issue a user warning here.
232  	  JALLOC_HELPER_FREE(mapsBuf);
233  	}
234  	
235  	jassert_internal::JAssert& jassert_internal::JAssert::jbacktrace ()
236  	{
237  	  writeBacktrace();
238  	  writeProcMaps();
239  	  // This goes to stdout.  Could also print to DUP_LOG_FD
240  	  Print( writeJbacktraceMsg() );
241  	  return *this;  // Needed as part of JASSERT macro
242  	}
243  	
244  	void jassert_internal::reset_on_fork ( )
245  	{
246  	  pthread_mutex_t newLock = PTHREAD_MUTEX_INITIALIZER;
247  	  logLock = newLock;
248  	}
249  	
250  	void jassert_internal::set_log_file ( const jalib::string& path )
251  	{
252  	  theLogFilePath() = path;
253  	  if ( theLogFileFd != -1 ) close ( theLogFileFd );
254  	  theLogFileFd = -1;
255  	  if ( path.length() > 0 )
256  	  {
257  	    theLogFileFd = _open_log_safe ( path, DUP_LOG_FD );
258  	    if ( theLogFileFd == -1 )
259  	      theLogFileFd = _open_log_safe ( path + "_2", DUP_LOG_FD );
260  	    if ( theLogFileFd == -1 )
261  	      theLogFileFd = _open_log_safe ( path + "_3", DUP_LOG_FD );
262  	    if ( theLogFileFd == -1 )
263  	      theLogFileFd = _open_log_safe ( path + "_4", DUP_LOG_FD );
264  	    if ( theLogFileFd == -1 )
265  	      theLogFileFd = _open_log_safe ( path + "_5", DUP_LOG_FD );
266  	  }
267  	}
268  	
269  	static int _initJassertOutputDevices()
270  	{
271  	  pthread_mutex_t newLock = PTHREAD_MUTEX_INITIALIZER;
272  	  logLock = newLock;
273  	
Event tainted_string_return_content: "getenv" returns tainted string content.
Event var_assign: Assigning: "errpath" = "getenv("JALIB_STDERR_PATH")", which taints "errpath".
Also see events: [tainted_string]
274  	  const char* errpath = getenv ( "JALIB_STDERR_PATH" );
275  	
276  	#ifdef DEBUG
At conditional (1): "errpath != NULL": Taking true branch.
At conditional (2): "theLogFileFd == -1": Taking true branch.
277  	  if ( errpath != NULL && theLogFileFd == -1 ) {
278  	    JASSERT_SET_LOGFILE ( jalib::XToString(getenv("DMTCP_TMPDIR"))
279  	                          + "/jassertlog." + jalib::XToString ( getpid() ) );
280  	  }
281  	#endif
282  	
At conditional (3): "errpath != NULL": Taking true branch.
283  	  if ( errpath != NULL )
Event tainted_string: Passing tainted string "errpath" to a function that cannot accept tainted data. [details]
Also see events: [tainted_string_return_content][var_assign]
284  	    errConsoleFd = _open_log_safe ( errpath, DUP_STDERR_FD );
285  	  else
286  	    errConsoleFd = dup2 ( fileno ( stderr ), DUP_STDERR_FD );
287  	
288  	  if( errConsoleFd == -1 ) {
289  	    jwrite ( fileno (stderr ), "dmtcp: cannot open output channel for error logging\n");
290  	    return false;
291  	  }
292  	  return true;
293  	}
294  	
295  	void jassert_internal::jassert_safe_print ( const char* str )
296  	{
297  	  static bool useErrorConsole = _initJassertOutputDevices();
298  	
299  	  if ( useErrorConsole )
300  	    jwrite ( errConsoleFd, str );
301  	
302  	  if ( theLogFileFd != -1 ) {
303  	    int rv = jwrite ( theLogFileFd, str );
304  	
305  	    if ( rv < 0 ) {
306  	      if ( useErrorConsole ) {
307  	        jwrite ( errConsoleFd, "JASSERT: write failed, reopening log file.\n" );
308  	      }
309  	      JASSERT_SET_LOGFILE ( theLogFilePath() );
310  	      if ( theLogFileFd != -1 ) {
311  	        jwrite ( theLogFileFd, "JASSERT: write failed, reopened log file:\n");
312  	        jwrite ( theLogFileFd, str );
313  	      }
314  	    }
315  	  }
316  	}