Merge bitcoin/bitcoin#24371: util: Fix ReadBinaryFile reading beyond maxsize

a84650ebd5 util: Fix ReadBinaryFile reading beyond maxsize (klementtan)

Pull request description:

  Currently `ReadBinaryFile` will read beyond `maxsize` if `maxsize` is not a multiple of `128` (size of buffer)

  This is due to `fread` being called with `count = 128` instead of `count = min(128, maxsize - retval.size()` at every iteration

  The following unit test will fail:
  ```cpp
  BOOST_AUTO_TEST_CASE(util_ReadWriteFile)
  {
    fs::path tmpfolder = m_args.GetDataDirBase();
    fs::path tmpfile = tmpfolder / "read_binary.dat";
    std::string expected_text(300,'c');
    {
        std::ofstream file{tmpfile};
        file << expected_text;
    }
    {
        // read half the contents in file
        auto [valid, text] = ReadBinaryFile(tmpfile, expected_text.size() / 2);
        BOOST_CHECK_EQUAL(text.size(), 150);
    }
  }
  ```
  Error:
  ```
  test/util_tests.cpp:2593: error: in "util_tests/util_ReadWriteFile": check text.size() == 150 has failed [256 != 150]
  ```

ACKs for top commit:
  laanwj:
    Code review ACK a84650ebd5
  theStack:
    Code-review ACK a84650ebd5

Tree-SHA512: 752eebe58bc2102dec199b6775f8c3304d899f0ce36d6a022a58e27b076ba945ccd572858b19137b769effd8c6de73a9277f641be24dfb17657fb7173ea0eda0
This commit is contained in:
MarcoFalke 2022-03-10 10:08:43 +01:00
commit 5e33620ad8
No known key found for this signature in database
GPG key ID: CE2B75697E69A548
2 changed files with 50 additions and 3 deletions

View file

@ -17,6 +17,7 @@
#include <util/message.h> // For MessageSign(), MessageVerify(), MESSAGE_MAGIC
#include <util/moneystr.h>
#include <util/overflow.h>
#include <util/readwritefile.h>
#include <util/spanparsing.h>
#include <util/strencodings.h>
#include <util/string.h>
@ -24,9 +25,10 @@
#include <util/vector.h>
#include <array>
#include <optional>
#include <fstream>
#include <limits>
#include <map>
#include <optional>
#include <stdint.h>
#include <string.h>
#include <thread>
@ -2592,4 +2594,49 @@ BOOST_AUTO_TEST_CASE(util_ParseByteUnits)
BOOST_CHECK(!ParseByteUnits("1x", noop));
}
BOOST_AUTO_TEST_CASE(util_ReadBinaryFile)
{
fs::path tmpfolder = m_args.GetDataDirBase();
fs::path tmpfile = tmpfolder / "read_binary.dat";
std::string expected_text;
for (int i = 0; i < 30; i++) {
expected_text += "0123456789";
}
{
std::ofstream file{tmpfile};
file << expected_text;
}
{
// read all contents in file
auto [valid, text] = ReadBinaryFile(tmpfile);
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(text, expected_text);
}
{
// read half contents in file
auto [valid, text] = ReadBinaryFile(tmpfile, expected_text.size() / 2);
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(text, expected_text.substr(0, expected_text.size() / 2));
}
{
// read from non-existent file
fs::path invalid_file = tmpfolder / "invalid_binary.dat";
auto [valid, text] = ReadBinaryFile(invalid_file);
BOOST_CHECK(!valid);
BOOST_CHECK(text.empty());
}
}
BOOST_AUTO_TEST_CASE(util_WriteBinaryFile)
{
fs::path tmpfolder = m_args.GetDataDirBase();
fs::path tmpfile = tmpfolder / "write_binary.dat";
std::string expected_text = "bitcoin";
auto valid = WriteBinaryFile(tmpfile, expected_text);
std::string actual_text;
std::ifstream file{tmpfile};
file >> actual_text;
BOOST_CHECK(valid);
BOOST_CHECK_EQUAL(actual_text, expected_text);
}
BOOST_AUTO_TEST_SUITE_END()

View file

@ -18,7 +18,7 @@ std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxs
std::string retval;
char buffer[128];
do {
const size_t n = fread(buffer, 1, sizeof(buffer), f);
const size_t n = fread(buffer, 1, std::min(sizeof(buffer), maxsize - retval.size()), f);
// Check for reading errors so we don't return any data if we couldn't
// read the entire file (or up to maxsize)
if (ferror(f)) {
@ -26,7 +26,7 @@ std::pair<bool,std::string> ReadBinaryFile(const fs::path &filename, size_t maxs
return std::make_pair(false,"");
}
retval.append(buffer, buffer+n);
} while (!feof(f) && retval.size() <= maxsize);
} while (!feof(f) && retval.size() < maxsize);
fclose(f);
return std::make_pair(true,retval);
}