From 4a1c9424d36386264ee1688f56995e35b71a1e04 Mon Sep 17 00:00:00 2001 From: dmiller Date: Sun, 15 Dec 2019 05:05:57 +0000 Subject: [PATCH] Replace localtime calls with thread-safe alternative. See #1834 --- nbase/configure | 11 +++++------ nbase/configure.ac | 5 +++-- nbase/nbase.h | 6 ++++++ nbase/nbase_time.c | 37 +++++++++++++++++++++++++++++++++++++ nmap.cc | 37 +++++++++++++++++++++---------------- nping/nping.cc | 10 +++++++--- osscan.cc | 9 ++++++--- output.cc | 26 +++++++++++++++++++------- service_scan.cc | 12 +++++++++--- timing.cc | 28 +++++++++++++++++++--------- 10 files changed, 132 insertions(+), 49 deletions(-) diff --git a/nbase/configure b/nbase/configure index 882233c58..f092e2132 100755 --- a/nbase/configure +++ b/nbase/configure @@ -4255,7 +4255,7 @@ fi done -for ac_func in usleep gettimeofday sleep +for ac_func in usleep gettimeofday sleep localtime_s localtime_r do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -4264,16 +4264,15 @@ if eval test \"x\$"$as_ac_var"\" = x"yes"; then : #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF -else - case " $LIBOBJS " in +fi +done + +case " $LIBOBJS " in *" nbase_time.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS nbase_time.$ac_objext" ;; esac -fi -done - ac_fn_c_check_func "$LINENO" "getopt_long_only" "ac_cv_func_getopt_long_only" if test "x$ac_cv_func_getopt_long_only" = xyes; then : diff --git a/nbase/configure.ac b/nbase/configure.ac index 996935347..37069da42 100644 --- a/nbase/configure.ac +++ b/nbase/configure.ac @@ -157,8 +157,9 @@ fi AC_CHECK_FUNCS( getopt getopt_long_only) -AC_CHECK_FUNCS(usleep gettimeofday sleep, , - [ AC_LIBOBJ([nbase_time]) ]) +AC_CHECK_FUNCS(usleep gettimeofday sleep localtime_s localtime_r) +dnl Some of these time functions are always needed +AC_LIBOBJ([nbase_time]) AC_CHECK_FUNC(getopt_long_only, , [ AC_LIBOBJ([getopt]) ]) diff --git a/nbase/nbase.h b/nbase/nbase.h index 294d69d1a..950a5d782 100644 --- a/nbase/nbase.h +++ b/nbase/nbase.h @@ -186,6 +186,7 @@ #include #include +#include #if HAVE_SYS_SELECT_H #include @@ -357,6 +358,7 @@ extern "C" int vsnprintf (char *, size_t, const char *, va_list); #define open _open #define stricmp _stricmp #define putenv _putenv +#define tzset _tzset #if !defined(__GNUC__) #define snprintf _snprintf @@ -453,6 +455,10 @@ void usleep(unsigned long usec); #endif #endif +/* localtime is not thread safe. This will use a thread safe alternative on + * supported platforms. */ +int n_localtime(const time_t *timer, struct tm *result); + /***************** String functions -- See nbase_str.c ******************/ /* I modified this conditional because !@# Redhat does not easily provide the prototype even though the function exists */ diff --git a/nbase/nbase_time.c b/nbase/nbase_time.c index cb5e6f582..685bbc25c 100644 --- a/nbase/nbase_time.c +++ b/nbase/nbase_time.c @@ -153,6 +153,43 @@ nanosleep(&ts, NULL); } #endif +/* Thread safe time stuff */ +#ifdef WIN32 +/* On Windows, use CRT function localtime_s: + * errno_t localtime_s( + * struct tm* const tmDest, + * time_t const* const sourceTime + * ); + */ +#define n_localtime(timer, result) localtime_s(result, timer) +#else +#include +int n_localtime(const time_t *timer, struct tm *result) { +#ifdef HAVE_LOCALTIME_S +/* C11 localtime_s similar to Posix localtime_r, but with validity checking: + * struct tm *localtime_s(const time_t *restrict time, struct tm *restrict result); + */ + struct tm *tmp = localtime_s(timer, result); +#else +#ifdef HAVE_LOCALTIME_R +/* POSIX localtime_r thread-safe localtime function: + * struct tm *localtime_r(const time_t *timep, struct tm *result); + */ + struct tm *tmp = localtime_r(timer, result); +#else +/* No thread-safe alternative; use localtime. */ + struct tm *tmp = localtime(timer); + if (tmp) + *result = *tmp; +#endif +#endif + if (!tmp) { + return errno; + } + return 0; +} +#endif + #ifdef WIN32 int gettimeofday(struct timeval *tv, struct timeval *tz) { diff --git a/nmap.cc b/nmap.cc index cf0042af9..6af19e955 100644 --- a/nmap.cc +++ b/nmap.cc @@ -556,7 +556,7 @@ public: } delayed_options; -struct tm *local_time; +struct tm local_time; static void test_file_name(const char *filename, const char *option) { if (filename[0] == '-' && filename[1] != '\0') { @@ -922,29 +922,29 @@ void parse_options(int argc, char **argv) { o.setXSLStyleSheet("https://svn.nmap.org/nmap/docs/nmap.xsl"); } else if (strcmp(long_options[option_index].name, "oN") == 0) { test_file_name(optarg, long_options[option_index].name); - delayed_options.normalfilename = logfilename(optarg, local_time); + delayed_options.normalfilename = logfilename(optarg, &local_time); } else if (strcmp(long_options[option_index].name, "oG") == 0 || strcmp(long_options[option_index].name, "oM") == 0) { test_file_name(optarg, long_options[option_index].name); - delayed_options.machinefilename = logfilename(optarg, local_time); + delayed_options.machinefilename = logfilename(optarg, &local_time); if (long_options[option_index].name[1] == 'M') delayed_options.warn_deprecated("oM", "oG"); } else if (strcmp(long_options[option_index].name, "oS") == 0) { test_file_name(optarg, long_options[option_index].name); - delayed_options.kiddiefilename = logfilename(optarg, local_time); + delayed_options.kiddiefilename = logfilename(optarg, &local_time); } else if (strcmp(long_options[option_index].name, "oH") == 0) { fatal("HTML output is not directly supported, though Nmap includes an XSL for transforming XML output into HTML. See the man page."); } else if (strcmp(long_options[option_index].name, "oX") == 0) { test_file_name(optarg, long_options[option_index].name); - delayed_options.xmlfilename = logfilename(optarg, local_time); + delayed_options.xmlfilename = logfilename(optarg, &local_time); } else if (strcmp(long_options[option_index].name, "oA") == 0) { char buf[MAXPATHLEN]; test_file_name(optarg, long_options[option_index].name); - Snprintf(buf, sizeof(buf), "%s.nmap", logfilename(optarg, local_time)); + Snprintf(buf, sizeof(buf), "%s.nmap", logfilename(optarg, &local_time)); delayed_options.normalfilename = strdup(buf); - Snprintf(buf, sizeof(buf), "%s.gnmap", logfilename(optarg, local_time)); + Snprintf(buf, sizeof(buf), "%s.gnmap", logfilename(optarg, &local_time)); delayed_options.machinefilename = strdup(buf); - Snprintf(buf, sizeof(buf), "%s.xml", logfilename(optarg, local_time)); + Snprintf(buf, sizeof(buf), "%s.xml", logfilename(optarg, &local_time)); delayed_options.xmlfilename = strdup(buf); } else if (strcmp(long_options[option_index].name, "thc") == 0) { log_write(LOG_STDOUT, "!!Greets to Van Hauser, Plasmoid, Skyper and the rest of THC!!\n"); @@ -1145,7 +1145,7 @@ void parse_options(int argc, char **argv) { case 'm': delayed_options.warn_deprecated("m", "oG"); test_file_name(optarg, "oG"); - delayed_options.machinefilename = logfilename(optarg, local_time); + delayed_options.machinefilename = logfilename(optarg, &local_time); break; case 'n': o.noresolve = true; @@ -1161,7 +1161,7 @@ void parse_options(int argc, char **argv) { case 'o': delayed_options.warn_deprecated("o", "oN"); test_file_name(optarg, "o"); - delayed_options.normalfilename = logfilename(optarg, local_time); + delayed_options.normalfilename = logfilename(optarg, &local_time); break; case 'P': if (!optarg) { @@ -1559,14 +1559,14 @@ void apply_delayed_options() { o.reason = true; // ISO 8601 date/time -- http://www.cl.cam.ac.uk/~mgk25/iso-time.html - if (strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M %Z", local_time) <= 0) + if (strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M %Z", &local_time) <= 0) fatal("Unable to properly format time"); log_write(LOG_STDOUT | LOG_SKID, "Starting %s %s ( %s ) at %s\n", NMAP_NAME, NMAP_VERSION, NMAP_URL, tbuf); if (o.verbose) { - if (local_time->tm_mon == 8 && local_time->tm_mday == 1) { - unsigned int a = (local_time->tm_year - 97)%100; - log_write(LOG_STDOUT | LOG_SKID, "Happy %d%s Birthday to Nmap, may it live to be %d!\n", local_time->tm_year - 97,(a>=11&&a<=13?"th":(a%10==1?"st":(a%10==2?"nd":(a%10==3?"rd":"th")))), local_time->tm_year + 3); - } else if (local_time->tm_mon == 11 && local_time->tm_mday == 25) { + if (local_time.tm_mon == 8 && local_time.tm_mday == 1) { + unsigned int a = (local_time.tm_year - 97)%100; + log_write(LOG_STDOUT | LOG_SKID, "Happy %d%s Birthday to Nmap, may it live to be %d!\n", local_time.tm_year - 97,(a>=11&&a<=13?"th":(a%10==1?"st":(a%10==2?"nd":(a%10==3?"rd":"th")))), local_time.tm_year + 3); + } else if (local_time.tm_mon == 11 && local_time.tm_mday == 25) { log_write(LOG_STDOUT | LOG_SKID, "Nmap wishes you a merry Christmas! Specify -sX for Xmas Scan (https://nmap.org/book/man-port-scanning-techniques.html).\n"); } } @@ -1819,6 +1819,7 @@ int nmap_main(int argc, char *argv[]) { char hostname[FQDN_LEN + 1] = ""; struct sockaddr_storage ss; size_t sslen; + int err; #ifdef LINUX /* Check for WSL and warn that things may not go well. */ @@ -1832,8 +1833,12 @@ int nmap_main(int argc, char *argv[]) { } #endif + tzset(); now = time(NULL); - local_time = localtime(&now); + err = n_localtime(&now, &local_time); + if (err) { + fatal("n_localtime failed: %s", strerror(err)); + } if (argc < 2){ printusage(); diff --git a/nping/nping.cc b/nping/nping.cc index 273ab1225..9de151a7b 100644 --- a/nping/nping.cc +++ b/nping/nping.cc @@ -165,7 +165,8 @@ void signal_handler(int signo); * probe mode, echo client or echo server). */ int main(int argc, char *argv[] ){ - struct tm *tm; /* For time display */ + struct tm tm; /* For time display */ + int err; time_t now; /* Stores current time */ char tbuf[128]; /* Stores current time as a string */ ArgParser a; /* Command line argument parser */ @@ -174,8 +175,11 @@ int main(int argc, char *argv[] ){ NpingTarget *t=NULL; /* Get current time */ + tzset(); now = time(NULL); - tm = localtime(&now); + err = n_localtime(&now, &tm); + if (err) + nping_fatal(QT_3,"Error in localtime: %s", strerror(err)); o.stats.startRuntime(); /* Some run-time tests to ensure everything works as expected */ @@ -202,7 +206,7 @@ int main(int argc, char *argv[] ){ o.validateOptions(); /* ISO 8601 date/time -- http://www.cl.cam.ac.uk/~mgk25/iso-time.html */ - if ( strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M %Z", tm) <= 0) + if ( strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M %Z", &tm) <= 0) nping_fatal(QT_3,"Unable to properly format time"); nping_print(QT_1, "\nStarting %s %s ( %s ) at %s", NPING_NAME, NPING_VERSION, NPING_URL, tbuf); diff --git a/osscan.cc b/osscan.cc index f1b1c2ca6..9f111c9ea 100644 --- a/osscan.cc +++ b/osscan.cc @@ -674,12 +674,15 @@ void WriteSInfo(char *ostr, int ostrlen, bool isGoodFP, enum dist_calc_method distance_calculation_method, const u8 *mac, int openTcpPort, int closedTcpPort, int closedUdpPort) { - struct tm *ltime; + struct tm ltime; + int err; time_t timep; char dsbuf[10], otbuf[8], ctbuf[8], cubuf[8], dcbuf[8]; char macbuf[16]; timep = time(NULL); - ltime = localtime(&timep); + err = n_localtime(&timep, <ime); + if (err) + error("Error in localtime: %s", strerror(err)); otbuf[0] = '\0'; if (openTcpPort != -1) @@ -704,7 +707,7 @@ void WriteSInfo(char *ostr, int ostrlen, bool isGoodFP, Snprintf(macbuf, sizeof(macbuf), "%%M=%02X%02X%02X", mac[0], mac[1], mac[2]); Snprintf(ostr, ostrlen, "SCAN(V=%s%%E=%s%%D=%d/%d%%OT=%s%%CT=%s%%CU=%s%%PV=%c%s%s%%G=%c%s%%TM=%X%%P=%s)", - NMAP_VERSION, engine_id, ltime->tm_mon + 1, ltime->tm_mday, + NMAP_VERSION, engine_id, err ? 0 : ltime.tm_mon + 1, err ? 0 : ltime.tm_mday, otbuf, ctbuf, cubuf, isipprivate(addr) ? 'Y' : 'N', dsbuf, dcbuf, isGoodFP ? 'Y' : 'N', macbuf, (int) timep, NMAP_PLATFORM); } diff --git a/output.cc b/output.cc index 612d96a5a..80e3bc1bf 100644 --- a/output.cc +++ b/output.cc @@ -645,16 +645,28 @@ void printportoutput(Target *currenths, PortList *plist) { if (o.verbose > 1 || o.debugging) { time_t tm_secs, tm_sece; - struct tm *tm; + struct tm tm; + int err; char tbufs[128]; tm_secs = currenths->StartTime(); tm_sece = currenths->EndTime(); - tm = localtime(&tm_secs); - if (strftime(tbufs, sizeof(tbufs), "%Y-%m-%d %H:%M:%S %Z", tm) <= 0) - fatal("Unable to properly format host start time"); - - log_write(LOG_PLAIN, "Scanned at %s for %lds\n", - tbufs, (long) (tm_sece - tm_secs)); + err = n_localtime(&tm_secs, &tm); + if (err) { + error("Error in localtime: %s", strerror(err)); + log_write(LOG_PLAIN, "Scanned for %lds\n", + (long) (tm_sece - tm_secs)); + } + else { + if (strftime(tbufs, sizeof(tbufs), "%Y-%m-%d %H:%M:%S %Z", &tm) <= 0) { + error("Unable to properly format host start time"); + log_write(LOG_PLAIN, "Scanned for %lds\n", + (long) (tm_sece - tm_secs)); + } + else { + log_write(LOG_PLAIN, "Scanned at %s for %lds\n", + tbufs, (long) (tm_sece - tm_secs)); + } + } } log_write(LOG_MACHINE, "Host: %s (%s)", currenths->targetipstr(), currenths->HostName()); diff --git a/service_scan.cc b/service_scan.cc index 57a92ed28..1fcc93970 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -1725,8 +1725,9 @@ void ServiceNFO::addToServiceFingerprint(const char *probeName, const u8 *resp, // the SF-PortXXXX-TCP stuff, etc int spaceneeded = respused * 5 + strlen(probeName) + 128; int srcidx; - struct tm *ltime; + struct tm ltime; time_t timep; + int err; char buf[128]; assert(resplen); @@ -1746,8 +1747,13 @@ void ServiceNFO::addToServiceFingerprint(const char *probeName, const u8 *resp, if (servicefplen == 0) { timep = time(NULL); - ltime = localtime(&timep); - Snprintf(buf, sizeof(buf), "SF-Port%hu-%s:V=%s%s%%I=%d%%D=%d/%d%%Time=%X%%P=%s", portno, proto2ascii_uppercase(proto), NMAP_VERSION, (tunnel == SERVICE_TUNNEL_SSL)? "%T=SSL" : "", o.version_intensity, ltime->tm_mon + 1, ltime->tm_mday, (int) timep, NMAP_PLATFORM); + err = n_localtime(&timep, <ime); + if (err) + error("Error in localtime: %s", strerror(err)); + Snprintf(buf, sizeof(buf), "SF-Port%hu-%s:V=%s%s%%I=%d%%D=%d/%d%%Time=%X%%P=%s", + portno, proto2ascii_uppercase(proto), NMAP_VERSION, + (tunnel == SERVICE_TUNNEL_SSL)? "%T=SSL" : "", o.version_intensity, + err ? 0 : ltime.tm_mon + 1, err ? 0 : ltime.tm_mday, (int) timep, NMAP_PLATFORM); addServiceString(buf, servicewrap); } diff --git a/timing.cc b/timing.cc index cc17ccd0d..c517e8634 100644 --- a/timing.cc +++ b/timing.cc @@ -688,7 +688,8 @@ bool ScanProgressMeter::printStats(double perc_done, struct timeval tvtmp; double time_left_s; time_t timet; - struct tm *ltime; + struct tm ltime; + int err; if (!now) { gettimeofday(&tvtmp, NULL); @@ -721,17 +722,17 @@ bool ScanProgressMeter::printStats(double perc_done, /* Get the estimated time of day at completion */ timet = last_est.tv_sec; - ltime = localtime(&timet); + err = n_localtime(&timet, <ime); - if (ltime) { + if (!err) { log_write(LOG_STDOUT, "%s Timing: About %.2f%% done; ETC: %02d:%02d (%.f:%02.f:%02.f remaining)\n", - scantypestr, perc_done * 100, ltime->tm_hour, ltime->tm_min, + scantypestr, perc_done * 100, ltime.tm_hour, ltime.tm_min, floor(time_left_s / 60.0 / 60.0), floor(fmod(time_left_s / 60.0, 60.0)), floor(fmod(time_left_s, 60.0))); } else { - log_write(LOG_STDERR, "Timing error: localtime(%f) is NULL\n", (double) timet); + log_write(LOG_STDERR, "Timing error: n_localtime(%f): %s\n", (double) timet, strerror(err)); log_write(LOG_STDOUT, "%s Timing: About %.2f%% done; ETC: Unknown (%.f:%02.f:%02.f remaining)\n", scantypestr, perc_done * 100, floor(time_left_s / 60.0 / 60.0), @@ -757,7 +758,8 @@ bool ScanProgressMeter::printStats(double perc_done, additional_info may be NULL if no additional information is necessary. */ bool ScanProgressMeter::beginOrEndTask(const struct timeval *now, const char *additional_info, bool beginning) { struct timeval tvtmp; - struct tm *tm; + struct tm tm; + int err; time_t tv_sec; if (!o.verbose) { @@ -770,9 +772,14 @@ bool ScanProgressMeter::beginOrEndTask(const struct timeval *now, const char *ad } tv_sec = now->tv_sec; - tm = localtime(&tv_sec); + err = n_localtime(&tv_sec, &tm); + if (err) + log_write(LOG_STDERR, "Timing error: n_localtime(%f): %s\n", (double) tv_sec, strerror(err)); if (beginning) { - log_write(LOG_STDOUT, "Initiating %s at %02d:%02d", scantypestr, tm->tm_hour, tm->tm_min); + if (!err) + log_write(LOG_STDOUT, "Initiating %s at %02d:%02d", scantypestr, tm.tm_hour, tm.tm_min); + else + log_write(LOG_STDOUT, "Initiating %s", scantypestr); xml_open_start_tag("taskbegin"); xml_attribute("task", "%s", scantypestr); xml_attribute("time", "%lu", (unsigned long) now->tv_sec); @@ -784,7 +791,10 @@ bool ScanProgressMeter::beginOrEndTask(const struct timeval *now, const char *ad xml_close_empty_tag(); xml_newline(); } else { - log_write(LOG_STDOUT, "Completed %s at %02d:%02d, %.2fs elapsed", scantypestr, tm->tm_hour, tm->tm_min, TIMEVAL_MSEC_SUBTRACT(*now, begin) / 1000.0); + if (!err) + log_write(LOG_STDOUT, "Completed %s at %02d:%02d, %.2fs elapsed", scantypestr, tm.tm_hour, tm.tm_min, TIMEVAL_MSEC_SUBTRACT(*now, begin) / 1000.0); + else + log_write(LOG_STDOUT, "Completed %s, %.2fs elapsed", scantypestr, TIMEVAL_MSEC_SUBTRACT(*now, begin) / 1000.0); xml_open_start_tag("taskend"); xml_attribute("task", "%s", scantypestr); xml_attribute("time", "%lu", (unsigned long) now->tv_sec);