Merge pull request #3612 from karim200003/fix/libc-fgets-nonpositive-size

fix(libc): fail fgets with a size of 0 or less like FreeBSD
This commit is contained in:
Dean 2026-10-12 17:33:20 +13:00 committed by GitHub
commit bb67fe1a34
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 38 additions and 10 deletions

View File

@ -42,7 +42,7 @@ class FileStream {
std::byte _reserved[256 - sizeof(GuestFilePrefix)]{};
std::FILE* _handle;
bool _dynamic;
bool encodingError = false;
bool guestError = false;
public:
explicit FileStream(std::FILE* handle, bool dynamic = false) : _handle(handle), _dynamic(dynamic) {
@ -72,7 +72,7 @@ public:
bool Reopen(const std::filesystem::path& filename, const char* mode) {
auto* previous = GetHandle();
_guest = {};
encodingError = false;
guestError = false;
#ifdef _WIN32
std::wstring wideMode;
for (const char character : std::string_view(mode)) wideMode.push_back(static_cast<unsigned char>(character));
@ -94,7 +94,7 @@ public:
_guest.readRemaining = 0;
_guest.writeRemaining = 0;
_guest.flags = static_cast<std::int16_t>((_guest.flags & ~0x60) |
(std::feof(GetHandle()) ? 0x20 : 0) | (std::ferror(GetHandle()) || encodingError ? 0x40 : 0));
(std::feof(GetHandle()) ? 0x20 : 0) | (std::ferror(GetHandle()) || guestError ? 0x40 : 0));
}
void SyncUnlockedStatus() {
@ -108,17 +108,17 @@ public:
const bool error = ::ferror_unlocked(GetHandle()) != 0;
#endif
_guest.flags = static_cast<std::int16_t>((_guest.flags & ~0x60) |
(endOfFile ? 0x20 : 0) | (error || encodingError ? 0x40 : 0));
(endOfFile ? 0x20 : 0) | (error || guestError ? 0x40 : 0));
}
void SetEncodingError() {
encodingError = true;
void SetError() {
guestError = true;
SyncStatus();
}
void ClearError() {
std::clearerr(GetHandle());
encodingError = false;
guestError = false;
SyncStatus();
}

View File

@ -27,7 +27,7 @@ std::int32_t APS5_VABI ungetwc_nid_postfix(std::int32_t value, FileStream* strea
if (value == -1) return -1;
if (value < 0 || value > 255) {
errno = 86;
stream->SetEncodingError();
stream->SetError();
return -1;
}
const int result = std::ungetc(value, GetNativeStream(stream));
@ -69,6 +69,11 @@ int APS5_VABI ungetc_nid_postfix(int value, FileStream* stream) {
return result;
}
char* APS5_VABI fgets_nid_postfix(char* buffer, int size, FileStream* stream) {
if (size <= 0) {
stream->SetError();
errno = 22;
return nullptr;
}
auto* result = std::fgets(buffer, size, GetNativeStream(stream));
stream->SyncStatus();
return result;

View File

@ -8,6 +8,7 @@
#include <fstream>
#include <iterator>
#include <initializer_list>
#include <limits>
#include <chrono>
#include <atomic>
#include <thread>
@ -121,7 +122,7 @@ static void CheckUnlockedStreams() {
Require(std::ungetc('Q', stream.GetHandle()) == 'Q');
Require(getc_unlocked_nid_postfix(&stream) == 'Q' && !(guest.flags & 0x20));
std::rewind(stream.GetHandle());
stream.SetEncodingError();
stream.SetError();
Require(putc_unlocked_nid_postfix('R', &stream) == 'R' && (guest.flags & 0x40));
clearerr_nid_postfix(&stream);
Require(!(guest.flags & 0x40));
@ -229,6 +230,27 @@ static void CheckUnlockedStreamLock(bool write) {
stream.Close();
}
static void CheckFgetsSize() {
FileStream stream(std::tmpfile());
Require(fwrite_nid_postfix("line\n", 1, 5, &stream) == 5);
rewind_nid_postfix(&stream);
for (const int size : {0, -1, std::numeric_limits<int>::min()}) {
char buffer[8];
std::memset(buffer, '!', sizeof(buffer));
*__error_nid_postfix() = 0;
Require(fgets_nid_postfix(buffer, size, &stream) == nullptr);
Require(*__error_nid_postfix() == 22 && ferror_nid_postfix(&stream) != 0 && feof_nid_postfix(&stream) == 0);
Require(buffer[0] == '!' && ftello_nid_postfix(&stream) == 0);
Require(ferror_nid_postfix(&stream) != 0);
clearerr_nid_postfix(&stream);
Require(ferror_nid_postfix(&stream) == 0);
}
char line[8]{};
Require(fgets_nid_postfix(line, sizeof(line), &stream) == line && std::strcmp(line, "line\n") == 0);
Require(ferror_nid_postfix(&stream) == 0);
stream.Close();
}
static void CheckRewind() {
FileStream stream(std::tmpfile());
Require(fwrite_nid_postfix("ab", 1, 2, &stream) == 2);
@ -239,7 +261,7 @@ static void CheckRewind() {
rewind_nid_postfix(&stream);
Require(fgetc_nid_postfix(&stream) == 'a' && fgetc_nid_postfix(&stream) == 'b' && fgetc_nid_postfix(&stream) == EOF);
Require(feof_nid_postfix(&stream) != 0);
stream.SetEncodingError();
stream.SetError();
Require(ferror_nid_postfix(&stream) != 0);
rewind_nid_postfix(&stream);
Require(feof_nid_postfix(&stream) == 0 && ferror_nid_postfix(&stream) == 0 && fgetc_nid_postfix(&stream) == 'a');
@ -511,6 +533,7 @@ int main() {
Require(fgetc_nid_postfix(&locked) == 'A' && fgetc_nid_postfix(&locked) == 'B');
locked.Close();
CheckRewind();
CheckFgetsSize();
CheckUnlockedStreams();
CheckUnlockedStreamLock(false);
CheckUnlockedStreamLock(true);