diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 62f3b7a43b9..953fa7e0c56 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -390,8 +390,9 @@ if(BUILD_TESTING) target_link_libraries(guest_standard_stream_tests PRIVATE libc) configure_windows_unwind(guest_standard_stream_tests) add_test(NAME guest_standard_streams COMMAND guest_standard_stream_tests) + add_test(NAME guest_standard_streams_closed_stdout COMMAND guest_standard_stream_tests --closed-stdout) if(WIN32) - set_tests_properties(guest_standard_streams PROPERTIES ENVIRONMENT_MODIFICATION + set_tests_properties(guest_standard_streams guest_standard_streams_closed_stdout PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() add_test_executable(file_streams_tests ${LIBS_INCLUDE_DIR}/tests/FileStreams.cpp) diff --git a/core/libs/prx/libc/include/FileStream.hpp b/core/libs/prx/libc/include/FileStream.hpp index 4ad9fa00b8b..0f51bc7d6d2 100644 --- a/core/libs/prx/libc/include/FileStream.hpp +++ b/core/libs/prx/libc/include/FileStream.hpp @@ -71,6 +71,10 @@ class FileStream { return _handle; } + bool IsOpen() const { + return _handle != nullptr; + } + bool IsDynamic() const { return _dynamic; } @@ -103,6 +107,7 @@ class FileStream { return true; } void SyncStatus() { + if (!_handle) return; _guest.readRemaining = 0; _guest.writeRemaining = 0; _guest.flags = static_cast((_guest.flags & ~0x60) | @@ -110,6 +115,7 @@ class FileStream { } void SyncUnlockedStatus() { + if (!_handle) return; _guest.readRemaining = 0; _guest.writeRemaining = 0; #ifdef _WIN32 @@ -129,6 +135,7 @@ class FileStream { } void ClearError() { + if (!_handle) return; std::clearerr(GetHandle()); guestError = false; SyncStatus(); diff --git a/core/libs/prx/libc/src/FileAndHeap.cpp b/core/libs/prx/libc/src/FileAndHeap.cpp index 69e7f8d0fe8..16025ecbb94 100644 --- a/core/libs/prx/libc/src/FileAndHeap.cpp +++ b/core/libs/prx/libc/src/FileAndHeap.cpp @@ -324,6 +324,11 @@ int APS5_VABI fopen_s_nid_postfix(FileStream** result, const char* filename, con } int APS5_VABI fclose_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { + std::unique_ptr owner(stream->IsDynamic() ? stream : nullptr); + errno = 9; + return -1; + } GetNativeStream(stream); std::unique_ptr owner(stream->IsDynamic() ? stream : nullptr); return stream->Close(); @@ -357,6 +362,7 @@ FileStream* APS5_VABI _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(const } size_t APS5_VABI fread_nid_postfix(void* buffer, size_t size, size_t count, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return 0; } auto* handle = GetNativeStream(stream); if (size == 0 || count == 0) return 0; if (!buffer) throw std::runtime_error("fread: null buffer"); @@ -371,6 +377,7 @@ size_t APS5_VABI fread_nid_postfix(void* buffer, size_t size, size_t count, File } size_t APS5_VABI fwrite_nid_postfix(const void* buffer, size_t size, size_t count, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return 0; } auto* handle = GetNativeStream(stream); if (size == 0 || count == 0) return 0; if (!buffer) throw std::runtime_error("fwrite: null buffer"); @@ -380,6 +387,7 @@ size_t APS5_VABI fwrite_nid_postfix(const void* buffer, size_t size, size_t coun } int APS5_VABI fseeko_nid_postfix(FileStream* stream, std::int64_t offset, int origin) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } if (origin != SEEK_SET && origin != SEEK_CUR && origin != SEEK_END) { errno = 22; return -1; } #ifdef _WIN32 auto* native = GetNativeStream(stream); @@ -396,6 +404,7 @@ int APS5_VABI fseeko_nid_postfix(FileStream* stream, std::int64_t offset, int or } std::int64_t APS5_VABI ftello_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } #ifdef _WIN32 auto* native = GetNativeStream(stream); if (IsPipeStream(native)) { errno = 29; return -1; } @@ -435,12 +444,14 @@ void APS5_VABI rewind_nid_postfix(FileStream* stream) { int APS5_VABI fputs_nid_postfix(const char* str, FileStream* stream) { if (!str) throw std::runtime_error("fputs: null string"); + if (stream && !stream->IsOpen()) { errno = 9; return -1; } const int result = std::fputs(str, GetNativeStream(stream)); stream->SyncStatus(); return result; } int APS5_VABI fflush_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } std::FILE* native = stream ? GetNativeStream(stream) : nullptr; #ifdef __linux__ if (native && !__fwriting(native)) return 0; diff --git a/core/libs/prx/libc/src/Formatting.cpp b/core/libs/prx/libc/src/Formatting.cpp index bc08460a33f..cb1d0d5798b 100644 --- a/core/libs/prx/libc/src/Formatting.cpp +++ b/core/libs/prx/libc/src/Formatting.cpp @@ -239,6 +239,7 @@ int APS5_VABI vasprintf_nid_postfix(char** destination, const char* format, VaLi } int APS5_VABI vfprintf_nid_postfix(FileStream* stream, const char* format, VaList* args) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } auto* native = GetNativeStream(stream); #ifdef _WIN32 std::string buffer; @@ -280,6 +281,7 @@ int APS5_VABI fprintf_nid_postfix(FileStream* stream, const char* format, ...) { } int APS5_VABI fscanf_nid_postfix(FileStream* stream, const char* format, ...) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } auto* native = GetNativeStream(stream); #ifdef _WIN32 __builtin_sysv_va_list args; @@ -300,6 +302,7 @@ int APS5_VABI fscanf_nid_postfix(FileStream* stream, const char* format, ...) { #ifdef _WIN32 int APS5_VABI printf_nid_postfix(const char* format, ...) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } __builtin_sysv_va_list args; __builtin_sysv_va_start(args, format); const int result = LibcDetail::PrintWindows(format, args); @@ -308,6 +311,7 @@ int APS5_VABI printf_nid_postfix(const char* format, ...) { } int APS5_VABI libc_printf_nid_postfix(const char* format, ...) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } __builtin_sysv_va_list args; __builtin_sysv_va_start(args, format); const int result = LibcDetail::PrintWindows(format, args); @@ -334,6 +338,7 @@ int APS5_VABI sprintf_nid_postfix(char* buffer, const char* format, ...) { #else int APS5_VABI printf_nid_postfix(const char* format, ...) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } std::va_list args; va_start(args, format); const int result = HasGuestWideFormat(format) ? LibcDetail::PrintWindows(format, args) : std::vprintf(format, args); @@ -342,6 +347,7 @@ int APS5_VABI printf_nid_postfix(const char* format, ...) { } int APS5_VABI libc_printf_nid_postfix(const char* format, ...) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } std::va_list args; va_start(args, format); const int result = HasGuestWideFormat(format) ? LibcDetail::PrintWindows(format, args) : std::vprintf(format, args); @@ -423,6 +429,7 @@ int APS5_VABI sscanf_s_nid_postfix(const char* buffer, const char* format, ...) #endif int APS5_VABI vprintf_nid_postfix(const char* str, VaList* c) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } #ifdef _WIN32 return LibcDetail::PrintWindows(str, c); #else @@ -541,6 +548,7 @@ int APS5_VABI printf_s_nid_postfix(const char* format, ...) { #endif int APS5_VABI puts_nid_postfix(const char* s) { + if (!_Stdout_nid_postfix.IsOpen()) { errno = 9; return -1; } return std::puts(s); } diff --git a/core/libs/prx/libc/src/StandardStreams.cpp b/core/libs/prx/libc/src/StandardStreams.cpp index 69e24de20c2..dc79864a596 100644 --- a/core/libs/prx/libc/src/StandardStreams.cpp +++ b/core/libs/prx/libc/src/StandardStreams.cpp @@ -15,6 +15,7 @@ FileStream* __stderrp_nid_postfix = &_Stderr_nid_postfix; int __isthreaded_nid_postfix = 1; int APS5_VABI fgetc_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } const int result = std::fgetc(GetNativeStream(stream)); stream->SyncStatus(); return result; @@ -36,6 +37,7 @@ std::int32_t APS5_VABI ungetwc_nid_postfix(std::int32_t value, FileStream* strea } int APS5_VABI getc_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); } int APS5_VABI getc_unlocked_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } #ifdef _WIN32 const int result = _fgetc_nolock(GetNativeStream(stream)); #else @@ -47,12 +49,14 @@ int APS5_VABI getc_unlocked_nid_postfix(FileStream* stream) { int APS5_VABI __srget_nid_postfix(FileStream* stream) { return fgetc_nid_postfix(stream); } int APS5_VABI getchar_nid_postfix() { return fgetc_nid_postfix(__stdinp_nid_postfix); } int APS5_VABI fputc_nid_postfix(int value, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } const int result = std::fputc(value, GetNativeStream(stream)); stream->SyncStatus(); return result; } int APS5_VABI putc_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); } int APS5_VABI putc_unlocked_nid_postfix(int value, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } #ifdef _WIN32 const int result = _fputc_nolock(value, GetNativeStream(stream)); #else @@ -64,11 +68,13 @@ int APS5_VABI putc_unlocked_nid_postfix(int value, FileStream* stream) { int APS5_VABI __swbuf_nid_postfix(int value, FileStream* stream) { return fputc_nid_postfix(value, stream); } int APS5_VABI putchar_nid_postfix(int value) { return fputc_nid_postfix(value, __stdoutp_nid_postfix); } int APS5_VABI ungetc_nid_postfix(int value, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } const int result = std::ungetc(value, GetNativeStream(stream)); stream->SyncStatus(); return result; } char* APS5_VABI fgets_nid_postfix(char* buffer, int size, FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return nullptr; } if (size <= 0) { stream->SetError(); errno = 22; @@ -90,6 +96,7 @@ void APS5_VABI clearerr_nid_postfix(FileStream* stream) { stream->ClearError(); } int APS5_VABI fileno_nid_postfix(FileStream* stream) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } #ifdef _WIN32 const int descriptor = _fileno(GetNativeStream(stream)); #else @@ -99,6 +106,7 @@ int APS5_VABI fileno_nid_postfix(FileStream* stream) { return descriptor; } int APS5_VABI setvbuf_nid_postfix(FileStream* stream, char* buffer, int mode, std::size_t size) { + if (stream && !stream->IsOpen()) { errno = 9; return -1; } if (mode < 0 || mode > 2) return -1; const int native = mode == 0 ? _IOFBF : mode == 1 ? _IOLBF : _IONBF; if (native != _IONBF && size == 0) { diff --git a/core/libs/tests/FileStreams.cpp b/core/libs/tests/FileStreams.cpp index ff80543263c..bb1361dd5cc 100644 --- a/core/libs/tests/FileStreams.cpp +++ b/core/libs/tests/FileStreams.cpp @@ -88,7 +88,7 @@ int main(int argc, char** argv) { ExpectException([] { fopen_nid_postfix(nullptr, "r"); }); FileStream closed(std::tmpfile()); closed.Close(); - ExpectException([&] { fflush_nid_postfix(&closed); }); + Require(fflush_nid_postfix(&closed) == -1 && errno == 9); Require(std::remove(argv[1]) == 0); errno = 0; Require(fopen_nid_postfix(argv[1], "rb") == nullptr); diff --git a/core/libs/tests/GuestStandardStreams.cpp b/core/libs/tests/GuestStandardStreams.cpp index 83b97d5ecd7..f1fc3f943ea 100644 --- a/core/libs/tests/GuestStandardStreams.cpp +++ b/core/libs/tests/GuestStandardStreams.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,10 @@ extern FileStream* __stdoutp_nid_postfix; extern FileStream* __stderrp_nid_postfix; extern int __isthreaded_nid_postfix; int APS5_VABI fprintf_nid_postfix(FileStream*, const char*, ...); +int APS5_VABI fputs_nid_postfix(const char*, FileStream*); +int APS5_VABI printf_nid_postfix(const char*, ...); +int APS5_VABI vprintf_nid_postfix(const char*, void*); +int APS5_VABI puts_nid_postfix(const char*); int APS5_VABI vfprintf_nid_postfix(FileStream*, const char*, void*); int APS5_VABI vsprintf_nid_postfix(char*, const char*, void*); int APS5_VABI fgetc_nid_postfix(FileStream*); @@ -80,6 +85,22 @@ static int APS5_VABI WriteFormatted(FileStream* stream, const char* format, ...) #endif return result; } +static int APS5_VABI PrintStandard(const char* format, ...) { +#ifdef _WIN32 + __builtin_sysv_va_list args; + __builtin_sysv_va_start(args, format); +#else + std::va_list args; + va_start(args, format); +#endif + const int result = vprintf_nid_postfix(format, args); +#ifdef _WIN32 + __builtin_sysv_va_end(args); +#else + va_end(args); +#endif + return result; +} static int APS5_VABI FormatString(char* buffer, const char* format, ...) { #ifdef _WIN32 __builtin_sysv_va_list args; @@ -493,7 +514,38 @@ static void CheckDescriptorAccessModes() { Require(std::filesystem::remove(filename)); } -int main() { +static void CheckClosedStreamWrites() { + FileStream stream(std::tmpfile()); + Require(freopen_nid_postfix("missing-closed-stream-dir/out.log", "w", &stream) == nullptr); + Require(fputs_nid_postfix("text", &stream) == -1 && *__error_nid_postfix() == 9); + Require(fprintf_nid_postfix(&stream, "%d", 1) == -1 && *__error_nid_postfix() == 9); + Require(fwrite_nid_postfix("text", 1, 4, &stream) == 0 && *__error_nid_postfix() == 9); + Require(fputc_nid_postfix('t', &stream) == -1 && *__error_nid_postfix() == 9); + Require(putc_unlocked_nid_postfix('t', &stream) == -1 && *__error_nid_postfix() == 9); + Require(fflush_nid_postfix(&stream) == -1 && *__error_nid_postfix() == 9); + char buffer[8] = {}; + Require(fread_nid_postfix(buffer, 1, sizeof(buffer), &stream) == 0 && *__error_nid_postfix() == 9); + Require(fgetc_nid_postfix(&stream) == -1 && *__error_nid_postfix() == 9); + Require(fgets_nid_postfix(buffer, sizeof(buffer), &stream) == nullptr && *__error_nid_postfix() == 9); + Require(fseeko_nid_postfix(&stream, 0, SEEK_SET) == -1 && *__error_nid_postfix() == 9); + Require(ftello_nid_postfix(&stream) == -1 && *__error_nid_postfix() == 9); + Require(fileno_nid_postfix(&stream) == -1 && *__error_nid_postfix() == 9); + Require(ferror_nid_postfix(&stream) == 0 && feof_nid_postfix(&stream) == 0); + Require(fclose_nid_postfix(&stream) == -1 && *__error_nid_postfix() == 9); +} + +static int CheckClosedStandardOutput() { + Require(freopen_nid_postfix("missing-closed-stream-dir/out.log", "w", __stdoutp_nid_postfix) == nullptr); + Require(printf_nid_postfix("%d", 1) == -1 && *__error_nid_postfix() == 9); + Require(puts_nid_postfix("text") == -1 && *__error_nid_postfix() == 9); + Require(PrintStandard("%d", 1) == -1 && *__error_nid_postfix() == 9); + Require(fputs_nid_postfix("text", __stdoutp_nid_postfix) == -1 && *__error_nid_postfix() == 9); + return 0; +} + +int main(int argc, char** argv) { + if (argc > 1 && std::string_view(argv[1]) == "--closed-stdout") return CheckClosedStandardOutput(); + CheckClosedStreamWrites(); CheckDescriptorAccessModes(); Require(_Getmbcurmax_nid_postfix() == 1 && _Getmbcurmax_nid_postfix() == ___mb_cur_max_nid_postfix()); CheckFlushWhileReading();