1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-10-17 04:46:35 +00:00

Do not rely on std::string::reserve

Passing an object with capacity through std::ostringstream may change
its capacity. Use malloc instead to make sure the memory is allocated.
This commit is contained in:
elsid 2025-10-12 10:57:49 +02:00
parent c75aed5175
commit ab4637f6c7
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40

View file

@ -8,10 +8,14 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <filesystem>
#include <format>
#include <fstream>
#include <memory>
#include <sstream>
#include <vector>
namespace Bsa
{
@ -19,6 +23,17 @@ namespace Bsa
{
using namespace ::testing;
struct Free
{
void operator()(void* ptr) const { std::free(ptr); }
};
struct Buffer
{
std::unique_ptr<char, Free> mData;
std::size_t mCapacity;
};
struct Header
{
uint32_t mFormat;
@ -69,13 +84,9 @@ namespace Bsa
std::format("{}.{}.bsa", testInfo->test_suite_name(), testInfo->name()));
}
std::string makeBsaBuffer(std::uint32_t fileSize, std::uint32_t fileOffset)
Buffer makeBsaBuffer(std::uint32_t fileSize, std::uint32_t fileOffset)
{
std::string buffer;
buffer.reserve(static_cast<std::size_t>(fileSize) + static_cast<std::size_t>(fileOffset) + 34);
std::ostringstream stream(std::move(buffer));
std::ostringstream stream;
const Header header{
.mFormat = static_cast<std::uint32_t>(BsaVersion::Uncompressed),
@ -98,7 +109,17 @@ namespace Bsa
writeArchive(archive, stream);
return std::move(stream).str();
const std::string data = std::move(stream).str();
const std::size_t capacity = static_cast<std::size_t>(fileSize) + static_cast<std::size_t>(fileOffset) + 34;
std::unique_ptr<char, Free> buffer(reinterpret_cast<char*>(std::malloc(capacity)));
if (buffer == nullptr)
throw std::bad_alloc();
std::memcpy(buffer.get(), data.data(), data.size());
return Buffer{ .mData = std::move(buffer), .mCapacity = capacity };
}
TEST(BSAFileTest, shouldHandleEmpty)
@ -266,11 +287,13 @@ namespace Bsa
TEST(BSAFileTest, shouldHandleSingleFileAtTheEndOfLargeFile)
{
constexpr std::uint32_t maxUInt32 = std::numeric_limits<uint32_t>::max();
const std::string buffer = makeBsaBuffer(maxUInt32, maxUInt32 - 34);
constexpr std::uint32_t fileSize = maxUInt32;
constexpr std::uint32_t fileOffset = maxUInt32 - 34;
const Buffer buffer = makeBsaBuffer(fileSize, fileOffset);
TestBSAFile file;
// Use capacity assuming we never read beyond small header.
Files::IMemStream stream(buffer.data(), buffer.capacity());
Files::IMemStream stream(buffer.mData.get(), buffer.mCapacity);
file.readHeader(stream);
std::vector<char> namesBuffer = { 'a', '\0' };
@ -289,11 +312,13 @@ namespace Bsa
TEST(BSAFileTest, shouldThrowExceptionOnTooBigAbsoluteOffset)
{
constexpr std::uint32_t maxUInt32 = std::numeric_limits<uint32_t>::max();
const std::string buffer = makeBsaBuffer(maxUInt32, maxUInt32 - 34 + 1);
constexpr std::uint32_t fileSize = maxUInt32;
constexpr std::uint32_t fileOffset = maxUInt32 - 34 + 1;
const Buffer buffer = makeBsaBuffer(fileSize, fileOffset);
TestBSAFile file;
// Use capacity assuming we never read beyond small header.
Files::IMemStream stream(buffer.data(), buffer.capacity());
Files::IMemStream stream(buffer.mData.get(), buffer.mCapacity);
EXPECT_THROW(file.readHeader(stream), std::runtime_error);
EXPECT_THAT(file.getList(), IsEmpty());