[libcxx] r282634 - Improve 'last_write_time(...)' accuracy and detect overflow errors.

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 28 14:16:58 PDT 2016


Author: ericwf
Date: Wed Sep 28 16:16:58 2016
New Revision: 282634

URL: http://llvm.org/viewvc/llvm-project?rev=282634&view=rev
Log:
Improve 'last_write_time(...)' accuracy and detect overflow errors.

The ::stat struct on Linux, FreeBSD, and OS X  provides the access and
modification times as an instance of 'timespec', which has a nanosecond
resolution. The 'st_mtime' and 'st_atime' members simply reference the 'tv_sec'
value of the timespec struct. This patch changes 'last_write_time(...)' so that
it extracts both the seconds and nanoseconds values of the last modification
time, providing a more accurate implementation of 'last_write_time(...)'.

Additionally this patch fixes a possible signed integer overflow bug. The
'file_time_type' type cannot represent all possible values returned by
the filesystem. Attempting to construct a 'file_time_type' from one of these
values is undefined behavior. This patch avoids that UB by detecting possible
overflows before the conversion.

Modified:
    libcxx/trunk/src/experimental/filesystem/operations.cpp

Modified: libcxx/trunk/src/experimental/filesystem/operations.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/src/experimental/filesystem/operations.cpp?rev=282634&r1=282633&r2=282634&view=diff
==============================================================================
--- libcxx/trunk/src/experimental/filesystem/operations.cpp (original)
+++ libcxx/trunk/src/experimental/filesystem/operations.cpp Wed Sep 28 16:16:58 2016
@@ -509,7 +509,6 @@ bool set_times_checked(time_t* sec_out,
     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
     if (subsec_dur.count() < 0) {
         if (sec_dur.count() > min_seconds) {
-
             sec_dur -= seconds(1);
             subsec_dur += seconds(1);
         } else {
@@ -520,11 +519,24 @@ bool set_times_checked(time_t* sec_out,
         && checked_set(subsec_out, subsec_dur.count());
 }
 
+using TimeSpec = struct ::timespec;
+using StatT = struct ::stat;
+
+#if defined(__APPLE__)
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
+TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
+#else
+TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
+__attribute__((unused)) // Suppress warning
+TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
+#endif
+
 }} // end namespace detail
 
 
 file_time_type __last_write_time(const path& p, std::error_code *ec)
 {
+    using namespace ::std::chrono;
     std::error_code m_ec;
     struct ::stat st;
     detail::posix_stat(p, st, &m_ec);
@@ -532,8 +544,25 @@ file_time_type __last_write_time(const p
         set_or_throw(m_ec, ec, "last_write_time", p);
         return file_time_type::min();
     }
-    if (ec) ec->clear();
-    return file_time_type::clock::from_time_t(st.st_mtime);
+#ifndef _LIBCPP_HAS_NO_INT128
+    using IntMax = __int128_t;
+#else
+    using IntMax = intmax_t;
+#endif
+    // FIXME: The value may not be representable as file_time_type. Fix
+    // file_time_type so it can represent all possible values returned by the
+    // filesystem. For now we do the calculation with the largest possible types
+    // and then truncate, this prevents signed integer overflow bugs.
+    auto ts = detail::extract_mtime(st);
+    const auto NsDur = duration<IntMax, nano>(ts.tv_nsec) + seconds(ts.tv_sec);
+    if (NsDur > file_time_type::max().time_since_epoch() ||
+        NsDur < file_time_type::min().time_since_epoch()) {
+        set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
+                     "last_write_time", p);
+        return file_time_type::min();
+    }
+     if (ec) ec->clear();
+    return file_time_type(duration_cast<file_time_type::duration>(NsDur));
 }
 
 void __last_write_time(const path& p, file_time_type new_time,
@@ -554,9 +583,10 @@ void __last_write_time(const path& p, fi
         set_or_throw(m_ec, ec, "last_write_time", p);
         return;
     }
+    auto atime = detail::extract_atime(st);
     struct ::timeval tbuf[2];
-    tbuf[0].tv_sec = st.st_atime;
-    tbuf[0].tv_usec = 0;
+    tbuf[0].tv_sec = atime.tv_sec;
+    tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
     const bool overflowed = !detail::set_times_checked<microseconds>(
         &tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
 




More information about the cfe-commits mailing list