diff options
| author | Leitwolf11 | 2026-09-28 01:10:14 +0300 |
|---|---|---|
| committer | Leitwolf11 | 2026-09-28 01:10:14 +0300 |
| commit | 97328fefe9650c84b594f1cea212c48562e1745b (patch) | |
| tree | 8905f56f9a2f337637d6ed9c86675b52829239b5 /src | |
| parent | fbd2ec1c12c3ea39df0b7af01b6ab11360302299 (diff) | |
| download | catch3-97328fefe9650c84b594f1cea212c48562e1745b.tar.gz | |
Harden Catch3 exit guards and pin build workflows
Diffstat (limited to 'src')
| -rw-r--r-- | src/catch2/catch_session.cpp | 102 | ||||
| -rw-r--r-- | src/catch2/catch_tostring.cpp | 30 |
2 files changed, 111 insertions, 21 deletions
diff --git a/src/catch2/catch_session.cpp b/src/catch2/catch_session.cpp index 78cb5a06..56612ba1 100644 --- a/src/catch2/catch_session.cpp +++ b/src/catch2/catch_session.cpp @@ -27,11 +27,23 @@ #include <catch2/internal/catch_istream.hpp> #include <cassert> +#include <cerrno> #include <cstdio> #include <cstdlib> +#include <cstring> #include <exception> #include <iomanip> +#if defined( _WIN32 ) +# include <fcntl.h> +# include <io.h> +# include <sys/stat.h> +#else +# include <fcntl.h> +# include <sys/stat.h> +# include <unistd.h> +#endif + namespace Catch { namespace { @@ -150,29 +162,93 @@ namespace Catch { getMutableRegistryHub().getMutableTestCaseRegistry().enableFilenameTags(); } - // Creates empty file at path. The path must be writable, we do not - // try to create directories in path because that's hard in C++14. + std::FILE* openGuardFile( std::string const& guardFilePath, + int& error ) { +#if defined( _MSC_VER ) + std::FILE* file = nullptr; + error = fopen_s( &file, guardFilePath.c_str(), "w" ); + return error ? nullptr : file; +#elif defined( _WIN32 ) + const int descriptor = _open( guardFilePath.c_str(), + _O_WRONLY | _O_CREAT | _O_TRUNC | + _O_BINARY | _O_NOINHERIT, + _S_IREAD | _S_IWRITE ); + if ( descriptor == -1 ) { + error = errno; + return nullptr; + } + std::FILE* const file = _fdopen( descriptor, "w" ); + if ( file == nullptr ) { + error = errno; + _close( descriptor ); + } + return file; +#else + int flags = O_WRONLY | O_CREAT | O_TRUNC; +# if defined( O_NONBLOCK ) + flags |= O_NONBLOCK; +# endif +# if defined( O_CLOEXEC ) + flags |= O_CLOEXEC; +# endif +# if defined( O_NOFOLLOW ) + // Do not let a stale guard-file symlink redirect this write. + flags |= O_NOFOLLOW; +# endif + const int descriptor = + ::open( guardFilePath.c_str(), flags, S_IRUSR | S_IWUSR ); + if ( descriptor == -1 ) { + error = errno; + return nullptr; + } + + struct stat fileStatus = {}; + if ( ::fstat( descriptor, &fileStatus ) != 0 ) { + error = errno; + ::close( descriptor ); + return nullptr; + } + if ( !S_ISREG( fileStatus.st_mode ) ) { + error = EINVAL; + ::close( descriptor ); + return nullptr; + } + // The creation mode is already owner-only; also tighten an + // existing regular guard file before it is reused. + if ( ::fchmod( descriptor, S_IRUSR | S_IWUSR ) != 0 ) { + error = errno; + ::close( descriptor ); + return nullptr; + } + + std::FILE* const file = ::fdopen( descriptor, "w" ); + if ( file == nullptr ) { + error = errno; + ::close( descriptor ); + } + return file; +#endif + } + + // Creates an owner-only guard file. The path must be writable; we do + // not create its parent directories. void setUpGuardFile( std::string const& guardFilePath ) { if ( !guardFilePath.empty() ) { -#if defined( _MSC_VER ) - std::FILE* file = nullptr; - if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) { - char msgBuffer[100]; - const auto err = errno; + int err = 0; + std::FILE* const file = openGuardFile( guardFilePath, err ); + if ( file == nullptr ) { + char msgBuffer[100] = {}; std::string errMsg; +#if defined( _MSC_VER ) if ( !strerror_s( msgBuffer, err ) ) { errMsg = msgBuffer; } else { errMsg = "Could not translate errno to a string"; } - #else - std::FILE* file = std::fopen( guardFilePath.c_str(), "w" ); - if ( !file ) { - const auto err = errno; - const char* errMsg = std::strerror( err ); + const char* const message = std::strerror( err ); + errMsg = message ? message : "Unknown error"; #endif - CATCH_RUNTIME_ERROR( "Could not open the exit guard file '" << guardFilePath << "' because '" << errMsg << "' (" << err << ')' ); diff --git a/src/catch2/catch_tostring.cpp b/src/catch2/catch_tostring.cpp index dad62410..1fbf3a54 100644 --- a/src/catch2/catch_tostring.cpp +++ b/src/catch2/catch_tostring.cpp @@ -64,26 +64,40 @@ namespace Detail { } std::string formatTimeT(std::time_t time) { -#ifdef _MSC_VER std::tm timeInfo = {}; +#ifdef _MSC_VER const auto err = gmtime_s( &timeInfo, &time ); if ( err ) { return "gmtime from provided timepoint has failed. This " "happens e.g. with pre-1970 dates using Microsoft libc"; } +#elif defined( __MINGW32__ ) + if ( gmtime_s( &timeInfo, &time ) != 0 ) { + return "gmtime from provided timepoint has failed"; + } +#elif defined( CATCH_PLATFORM_PLAYSTATION ) + gmtime_s( &time, &timeInfo ); +#elif defined( __IAR_SYSTEMS_ICC__ ) + // This toolchain does not provide gmtime_r. Copy the result immediately + // so the formatter does not retain the C library's static buffer. + const std::tm* const converted = std::gmtime( &time ); + if ( converted == nullptr ) { + return "gmtime from provided timepoint has failed"; + } + timeInfo = *converted; #else - std::tm* timeInfo = std::gmtime( &time ); + if ( gmtime_r( &time, &timeInfo ) == nullptr ) { + return "gmtime from provided timepoint has failed"; + } #endif auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" ); - char timeStamp[timeStampSize]; + char timeStamp[timeStampSize] = {}; const char* const fmt = "%Y-%m-%dT%H:%M:%SZ"; -#ifdef _MSC_VER - std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ); -#else - std::strftime( timeStamp, timeStampSize, fmt, timeInfo ); -#endif + if ( std::strftime( timeStamp, timeStampSize, fmt, &timeInfo ) == 0 ) { + return "gmtime from provided timepoint has failed"; + } return std::string( timeStamp, timeStampSize - 1 ); } |