Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/catch2/catch_session.cpp
diff options
context:
space:
mode:
authorLeitwolf112026-09-28 01:10:14 +0300
committerLeitwolf112026-09-28 01:10:14 +0300
commit97328fefe9650c84b594f1cea212c48562e1745b (patch)
tree8905f56f9a2f337637d6ed9c86675b52829239b5 /src/catch2/catch_session.cpp
parentfbd2ec1c12c3ea39df0b7af01b6ab11360302299 (diff)
downloadcatch3-97328fefe9650c84b594f1cea212c48562e1745b.tar.gz
Harden Catch3 exit guards and pin build workflows
Diffstat (limited to 'src/catch2/catch_session.cpp')
-rw-r--r--src/catch2/catch_session.cpp102
1 files changed, 89 insertions, 13 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 << ')' );