diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp index 08df2ebbd..27813a6c2 100644 --- a/libc/stdio/stdio.cpp +++ b/libc/stdio/stdio.cpp @@ -1129,7 +1129,9 @@ size_t fread_unlocked(void* buf, size_t size, size_t count, FILE* fp) { // Read directly into the caller's buffer. while (total > 0) { - ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, total); + // The _read function pointer takes an int instead of a size_t. + int chunk_size = MIN(total, INT_MAX); + ssize_t bytes_read = (*fp->_read)(fp->_cookie, dst, chunk_size); if (bytes_read <= 0) { fp->_flags |= (bytes_read == 0) ? __SEOF : __SERR; break; diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index 87031f6fc..5bc65679a 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -2938,3 +2939,27 @@ TEST(STDIO_TEST, freopen_null_filename_mode) { fclose(fp); } + +static int64_t GetTotalRamGiB() { + struct sysinfo si; + sysinfo(&si); + return (static_cast(si.totalram) * si.mem_unit) / 1024 / 1024 / 1024; +} + +TEST(STDIO_TEST, fread_int_overflow) { + if (GetTotalRamGiB() <= 4) GTEST_SKIP() << "not enough memory"; + + const size_t too_big_for_an_int = 0x80000000ULL; + std::vector buf(too_big_for_an_int); + std::unique_ptr fp{fopen("/dev/zero", "re"), fclose}; + ASSERT_EQ(too_big_for_an_int, fread(&buf[0], 1, too_big_for_an_int, fp.get())); +} + +TEST(STDIO_TEST, fwrite_int_overflow) { + if (GetTotalRamGiB() <= 4) GTEST_SKIP() << "not enough memory"; + + const size_t too_big_for_an_int = 0x80000000ULL; + std::vector buf(too_big_for_an_int); + std::unique_ptr fp{fopen("/dev/null", "we"), fclose}; + ASSERT_EQ(too_big_for_an_int, fwrite(&buf[0], 1, too_big_for_an_int, fp.get())); +}