Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,4 @@ Version 3.4.0 - 2026 ???
- Fix execute_many() to clear stale bindings between parameter sets (#554)
- Fix Column::operator<< to stream the exact column bytes via getString() (#556)
- Restore the Coverity Scan static analysis as a GitHub Actions workflow, replacing the old Travis CI job
- Fix Database::getHeaderInfo() signed-shift UB and use fixed-width types for the Header struct (#558)
45 changes: 23 additions & 22 deletions include/SQLiteCpp/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ namespace filesystem = experimental::filesystem;

#endif // SQLITECPP_HAVE_STD_EXPERIMENTAL_FILESYSTEM

#include <cstdint>
#include <memory>
#include <string.h>

Expand Down Expand Up @@ -120,28 +121,28 @@ SQLITECPP_API int getLibVersionNumber() noexcept;
// Public structure for representing all fields contained within the SQLite header.
// Official documentation for fields: https://www.sqlite.org/fileformat.html#the_database_header
struct Header {
unsigned char headerStr[16];
unsigned int pageSizeBytes;
unsigned char fileFormatWriteVersion;
unsigned char fileFormatReadVersion;
unsigned char reservedSpaceBytes;
unsigned char maxEmbeddedPayloadFrac;
unsigned char minEmbeddedPayloadFrac;
unsigned char leafPayloadFrac;
unsigned long fileChangeCounter;
unsigned long databaseSizePages;
unsigned long firstFreelistTrunkPage;
unsigned long totalFreelistPages;
unsigned long schemaCookie;
unsigned long schemaFormatNumber;
unsigned long defaultPageCacheSizeBytes;
unsigned long largestBTreePageNumber;
unsigned long databaseTextEncoding;
unsigned long userVersion;
unsigned long incrementalVaccumMode;
unsigned long applicationId;
unsigned long versionValidFor;
unsigned long sqliteVersion;
std::uint8_t headerStr[16];
std::uint32_t pageSizeBytes;
std::uint8_t fileFormatWriteVersion;
std::uint8_t fileFormatReadVersion;
std::uint8_t reservedSpaceBytes;
std::uint8_t maxEmbeddedPayloadFrac;
std::uint8_t minEmbeddedPayloadFrac;
std::uint8_t leafPayloadFrac;
std::uint32_t fileChangeCounter;
std::uint32_t databaseSizePages;
std::uint32_t firstFreelistTrunkPage;
std::uint32_t totalFreelistPages;
std::uint32_t schemaCookie;
std::uint32_t schemaFormatNumber;
std::uint32_t defaultPageCacheSizeBytes;
std::uint32_t largestBTreePageNumber;
std::uint32_t databaseTextEncoding;
std::uint32_t userVersion;
std::uint32_t incrementalVaccumMode;
std::uint32_t applicationId;
std::uint32_t versionValidFor;
std::uint32_t sqliteVersion;
};

/**
Expand Down
108 changes: 23 additions & 85 deletions src/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,97 +329,35 @@ Header Database::getHeaderInfo(const std::string& aFilename)
throw SQLite::Exception("Invalid or encrypted SQLite header in file " + aFilename);
}

h.pageSizeBytes = (buf[16] << 8) | buf[17];
const auto readBE32 = [&buf](std::size_t offset) -> std::uint32_t
{
return (static_cast<std::uint32_t>(buf[offset]) << 24) |
(static_cast<std::uint32_t>(buf[offset + 1]) << 16) |
(static_cast<std::uint32_t>(buf[offset + 2]) << 8) |
(static_cast<std::uint32_t>(buf[offset + 3]) << 0);
};

h.pageSizeBytes = (static_cast<std::uint32_t>(buf[16]) << 8) | static_cast<std::uint32_t>(buf[17]);
h.fileFormatWriteVersion = buf[18];
h.fileFormatReadVersion = buf[19];
h.reservedSpaceBytes = buf[20];
h.maxEmbeddedPayloadFrac = buf[21];
h.minEmbeddedPayloadFrac = buf[22];
h.leafPayloadFrac = buf[23];

h.fileChangeCounter =
(buf[24] << 24) |
(buf[25] << 16) |
(buf[26] << 8) |
(buf[27] << 0);

h.databaseSizePages =
(buf[28] << 24) |
(buf[29] << 16) |
(buf[30] << 8) |
(buf[31] << 0);

h.firstFreelistTrunkPage =
(buf[32] << 24) |
(buf[33] << 16) |
(buf[34] << 8) |
(buf[35] << 0);

h.totalFreelistPages =
(buf[36] << 24) |
(buf[37] << 16) |
(buf[38] << 8) |
(buf[39] << 0);

h.schemaCookie =
(buf[40] << 24) |
(buf[41] << 16) |
(buf[42] << 8) |
(buf[43] << 0);

h.schemaFormatNumber =
(buf[44] << 24) |
(buf[45] << 16) |
(buf[46] << 8) |
(buf[47] << 0);

h.defaultPageCacheSizeBytes =
(buf[48] << 24) |
(buf[49] << 16) |
(buf[50] << 8) |
(buf[51] << 0);

h.largestBTreePageNumber =
(buf[52] << 24) |
(buf[53] << 16) |
(buf[54] << 8) |
(buf[55] << 0);

h.databaseTextEncoding =
(buf[56] << 24) |
(buf[57] << 16) |
(buf[58] << 8) |
(buf[59] << 0);

h.userVersion =
(buf[60] << 24) |
(buf[61] << 16) |
(buf[62] << 8) |
(buf[63] << 0);

h.incrementalVaccumMode =
(buf[64] << 24) |
(buf[65] << 16) |
(buf[66] << 8) |
(buf[67] << 0);

h.applicationId =
(buf[68] << 24) |
(buf[69] << 16) |
(buf[70] << 8) |
(buf[71] << 0);

h.versionValidFor =
(buf[92] << 24) |
(buf[93] << 16) |
(buf[94] << 8) |
(buf[95] << 0);

h.sqliteVersion =
(buf[96] << 24) |
(buf[97] << 16) |
(buf[98] << 8) |
(buf[99] << 0);
h.fileChangeCounter = readBE32(24);
h.databaseSizePages = readBE32(28);
h.firstFreelistTrunkPage = readBE32(32);
h.totalFreelistPages = readBE32(36);
h.schemaCookie = readBE32(40);
h.schemaFormatNumber = readBE32(44);
h.defaultPageCacheSizeBytes = readBE32(48);
h.largestBTreePageNumber = readBE32(52);
h.databaseTextEncoding = readBE32(56);
h.userVersion = readBE32(60);
h.incrementalVaccumMode = readBE32(64);
h.applicationId = readBE32(68);
h.versionValidFor = readBE32(92);
h.sqliteVersion = readBE32(96);

return h;
}
Expand Down
Loading