From 0019a351d05c8355b0ab398b9cec9f846657f4b5 Mon Sep 17 00:00:00 2001 From: freyers Date: Fri, 15 May 2026 21:10:46 +0000 Subject: [PATCH] fix(fileutils): prevent stack buffer overflow in AbsPath getcwd + path concatenation and the absolute-path strcpy wrote into fixed PATH_MAX buffers with no bounds check, allowing a stack overflow for long paths. Build the working buffer with bounded snprintf, bail out safely (empty result) when the path cannot fit, and pass the output buffer size explicitly. Add regression tests for overlong relative and absolute paths. --- CcpFileUtils.cpp | 31 +++++++++++++++++++++---------- tests/CcpFileUtils.cpp | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CcpFileUtils.cpp b/CcpFileUtils.cpp index ed59d96..8b6bb49 100644 --- a/CcpFileUtils.cpp +++ b/CcpFileUtils.cpp @@ -217,6 +217,7 @@ bool CcpRenameFile( const std::wstring& src, const std::wstring& dst ) #include #include #include +#include #ifdef __APPLE__ #include #endif @@ -357,7 +358,7 @@ std::wstring CcpExecutablePath() namespace { -void AbsPath( const char* path, char* realPath ) +void AbsPath( const char* path, char* realPath, size_t realPathSize ) { char src[PATH_MAX]; if( path[0] != '/' ) @@ -366,20 +367,26 @@ void AbsPath( const char* path, char* realPath ) // working directory if( !getcwd( src, sizeof( src ) ) ) { - strcpy( realPath, path ); + strncpy_s( realPath, realPathSize, path, _TRUNCATE ); return; } - auto len = strlen( src ); - if( len > 0 && path[0] != 0 && src[len - 1] != '/' ) + size_t len = strlen( src ); + const char* separator = ( len > 0 && path[0] != 0 && src[len - 1] != '/' ) ? "/" : ""; + int written = snprintf( src + len, sizeof( src ) - len, "%s%s", separator, path ); + if( written < 0 || static_cast( written ) >= sizeof( src ) - len ) { - src[len] = '/'; - src[len + 1] = 0; + realPath[0] = 0; + return; } - strcat( src, path ); } else { + if( strlen( path ) >= sizeof( src ) ) + { + realPath[0] = 0; + return; + } strcpy( src, path ); } @@ -432,6 +439,12 @@ void AbsPath( const char* path, char* realPath ) ++end; } + if( strlen( src ) + 1 > realPathSize ) + { + realPath[0] = 0; + return; + } + // Put initial slashes back in end = realPath; for( size_t i = 0; i < initialSlashes; ++i ) @@ -463,10 +476,8 @@ std::wstring CcpGetAbsolutePath( const std::wstring& name ) { return L""; } - auto nameA = CW2A( name.c_str() ); char buffer[PATH_MAX]; - strcpy_s( buffer, nameA ); - AbsPath( CW2A( name.c_str() ), buffer ); + AbsPath( CW2A( name.c_str() ), buffer, sizeof( buffer ) ); std::wstring absName = std::wstring( CA2W( buffer ) ); if( CcpIsPathDirectory( absName.c_str() ) && !absName.empty() && absName[absName.length() - 1] != L'/' ) { diff --git a/tests/CcpFileUtils.cpp b/tests/CcpFileUtils.cpp index 92a91cf..9a60ac9 100644 --- a/tests/CcpFileUtils.cpp +++ b/tests/CcpFileUtils.cpp @@ -126,3 +126,21 @@ TEST( CcpFileUtils, CcpGetAbsolutePathResolvesToNewFile ) unlink( tempFileName ); #endif } + +#if !_WIN32 + +TEST( CcpFileUtils, CcpGetAbsolutePathHandlesOverlongRelativePathSafely ) +{ + std::wstring huge( 64 * 1024, L'a' ); + auto path = CcpGetAbsolutePath( huge ); + EXPECT_TRUE( path.empty() ); +} + +TEST( CcpFileUtils, CcpGetAbsolutePathHandlesOverlongAbsolutePathSafely ) +{ + std::wstring huge = ROOT_PATH + std::wstring( 64 * 1024, L'a' ); + auto path = CcpGetAbsolutePath( huge ); + EXPECT_TRUE( path.empty() ); +} + +#endif