1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sql/recover_module/btree.h"
#include <algorithm>
#include <limits>
#include <ostream>
#include <type_traits>
#include "base/check_op.h"
#include "sql/recover_module/integers.h"
#include "sql/recover_module/pager.h"
#include "third_party/sqlite/sqlite3.h"
namespace sql {
namespace recover {
namespace {
// The SQLite database format is documented at the following URLs.
// https://www.sqlite.org/fileformat.html
// https://www.sqlite.org/fileformat2.html
constexpr uint8_t kInnerTablePageType = 0x05;
constexpr uint8_t kLeafTablePageType = 0x0D;
// Offset from the page header to the page type byte.
constexpr int kPageTypePageOffset = 0;
// Offset from the page header to the 2-byte cell count.
constexpr int kCellCountPageOffset = 3;
// Offset from an inner page header to the 4-byte last child page ID.
constexpr int kLastChildIdInnerPageOffset = 8;
// Offset from an inner page header to the cell pointer array.
constexpr int kFirstCellOfsetInnerPageOffset = 12;
// Offset from a leaf page header to the cell pointer array.
constexpr int kFirstCellOfsetLeafPageOffset = 8;
} // namespace
#if !DCHECK_IS_ON()
// In DCHECKed builds, the decoder contains a sequence checker, which has a
// non-trivial destructor.
static_assert(std::is_trivially_destructible<InnerPageDecoder>::value,
"Move the destructor to the .cc file if it's non-trival");
#endif // !DCHECK_IS_ON()
InnerPageDecoder::InnerPageDecoder(DatabasePageReader* db_reader) noexcept
: page_id_(db_reader->page_id()),
db_reader_(db_reader),
cell_count_(ComputeCellCount(db_reader)),
next_read_index_(0) {
DCHECK(IsOnValidPage(db_reader));
DCHECK(DatabasePageReader::IsValidPageId(page_id_));
}
int InnerPageDecoder::TryAdvance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(CanAdvance());
const int sqlite_status = db_reader_->ReadPage(page_id_);
if (sqlite_status != SQLITE_OK) {
// TODO(pwnall): UMA the error code.
next_read_index_ = cell_count_ + 1; // End the reading process.
return DatabasePageReader::kInvalidPageId;
}
const uint8_t* const page_data = db_reader_->page_data();
const int read_index = next_read_index_;
next_read_index_ += 1;
if (read_index == cell_count_)
return LoadBigEndianInt32(page_data + kLastChildIdInnerPageOffset);
const int cell_pointer_offset =
kFirstCellOfsetInnerPageOffset + (read_index << 1);
DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size())
<< "ComputeCellCount() used an incorrect upper bound";
const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset);
static_assert(std::numeric_limits<uint16_t>::max() + 4 <
std::numeric_limits<int>::max(),
"The addition below may overflow");
if (cell_pointer + 4 >= db_reader_->page_size()) {
// Each cell needs 1 byte for the rowid varint, in addition to the 4 bytes
// for the child page number that will be read below. Skip cells that
// obviously go over the page end.
return DatabasePageReader::kInvalidPageId;
}
if (cell_pointer < kFirstCellOfsetInnerPageOffset) {
// The pointer points into the cell's header.
return DatabasePageReader::kInvalidPageId;
}
return LoadBigEndianInt32(page_data + cell_pointer);
}
// static
bool InnerPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) {
static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize,
"The check below may perform an out-of-bounds memory access");
return db_reader->page_data()[kPageTypePageOffset] == kInnerTablePageType;
}
// static
int InnerPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) {
// The B-tree page header stores the cell count.
int header_count =
LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset);
static_assert(
kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize,
"The read above may be out of bounds");
// However, the data may be corrupted. So, use an upper bound based on the
// fact that the cell pointer array should never extend past the end of the
// page.
//
// The page size is always even, because it is either a power of two, for
// most pages, or a power of two minus 100, for the first database page. The
// cell pointer array starts at offset 12. So, each cell pointer must be
// separated from the page buffer's end by an even number of bytes.
DCHECK((db_reader->page_size() - kFirstCellOfsetInnerPageOffset) % 2 == 0);
int upper_bound =
(db_reader->page_size() - kFirstCellOfsetInnerPageOffset) >> 1;
static_assert(
kFirstCellOfsetInnerPageOffset <= DatabasePageReader::kMinUsablePageSize,
"The |upper_bound| computation above may overflow");
return std::min(header_count, upper_bound);
}
#if !DCHECK_IS_ON()
// In DCHECKed builds, the decoder contains a sequence checker, which has a
// non-trivial destructor.
static_assert(std::is_trivially_destructible<LeafPageDecoder>::value,
"Move the destructor to the .cc file if it's non-trival");
#endif // !DCHECK_IS_ON()
LeafPageDecoder::LeafPageDecoder(DatabasePageReader* db_reader) noexcept
: page_id_(db_reader->page_id()),
db_reader_(db_reader),
cell_count_(ComputeCellCount(db_reader)),
next_read_index_(0),
last_record_size_(0) {
DCHECK(IsOnValidPage(db_reader));
DCHECK(DatabasePageReader::IsValidPageId(page_id_));
}
bool LeafPageDecoder::TryAdvance() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(CanAdvance());
#if DCHECK_IS_ON()
// DCHECKs use last_record_size == 0 to check for incorrect access to the
// decoder's state.
last_record_size_ = 0;
#endif // DCHECK_IS_ON()
const int sqlite_status = db_reader_->ReadPage(page_id_);
if (sqlite_status != SQLITE_OK) {
// TODO(pwnall): UMA the error code.
next_read_index_ = cell_count_; // End the reading process.
return false;
}
const uint8_t* page_data = db_reader_->page_data();
const int read_index = next_read_index_;
next_read_index_ += 1;
const int cell_pointer_offset =
kFirstCellOfsetLeafPageOffset + (read_index << 1);
DCHECK_LE(cell_pointer_offset + 2, db_reader_->page_size())
<< "ComputeCellCount() used an incorrect upper bound";
const int cell_pointer = LoadBigEndianUint16(page_data + cell_pointer_offset);
static_assert(std::numeric_limits<uint16_t>::max() + 3 <
std::numeric_limits<int>::max(),
"The addition below may overflow");
if (cell_pointer + 3 >= db_reader_->page_size()) {
// Each cell needs at least 1 byte for page type varint, 1 byte for the
// rowid varint, and 1 byte for the record header size varint. Skip cells
// that obviously go over the page end.
return false;
}
if (cell_pointer < kFirstCellOfsetLeafPageOffset) {
// The pointer points into the cell's header.
return false;
}
const uint8_t* const cell_start = page_data + cell_pointer;
const uint8_t* const page_end = page_data + db_reader_->page_size();
DCHECK_LT(cell_start, page_end) << "Failed to skip over empty cells";
const uint8_t* rowid_start;
std::tie(last_record_size_, rowid_start) = ParseVarint(cell_start, page_end);
if (rowid_start == page_end) {
// The value size varint extended to the end of the page, so the rowid
// varint starts past the page end.
return false;
}
if (last_record_size_ <= 0) {
// Each payload needs at least one varint. Skip empty payloads.
#if DCHECK_IS_ON()
// DCHECKs use last_record_size == 0 to check for incorrect access to the
// decoder's state.
last_record_size_ = 0;
#endif // DCHECK_IS_ON()
return false;
}
const uint8_t* record_start;
std::tie(last_record_rowid_, record_start) =
ParseVarint(rowid_start, page_end);
if (record_start == page_end) {
// The rowid varint extended to the end of the page, so the record starts
// past the page end. Records need at least 1 byte for their header size
// varint, so this suggests corruption.
last_record_size_ = 0;
return false;
}
last_record_offset_ = record_start - page_data;
return true;
}
// static
bool LeafPageDecoder::IsOnValidPage(DatabasePageReader* db_reader) {
static_assert(kPageTypePageOffset < DatabasePageReader::kMinUsablePageSize,
"The check below may perform an out-of-bounds memory access");
return db_reader->page_data()[kPageTypePageOffset] == kLeafTablePageType;
}
// static
int LeafPageDecoder::ComputeCellCount(DatabasePageReader* db_reader) {
// See InnerPageDecoder::ComputeCellCount() for the reasoning behind the code.
int header_count =
LoadBigEndianUint16(db_reader->page_data() + kCellCountPageOffset);
static_assert(
kCellCountPageOffset + 2 <= DatabasePageReader::kMinUsablePageSize,
"The read above may be out of bounds");
int upper_bound =
(db_reader->page_size() - kFirstCellOfsetLeafPageOffset) >> 1;
static_assert(
kFirstCellOfsetLeafPageOffset <= DatabasePageReader::kMinUsablePageSize,
"The |upper_bound| computation above may overflow");
return std::min(header_count, upper_bound);
}
} // namespace recover
} // namespace sql
|