Skip to content

Commit 32608ab

Browse files
committed
Fix cell payload overflow.
Added logic to calculate cell payload overflow thresholds from the SQLite spec.
1 parent f9ff393 commit 32608ab

File tree

2 files changed

+49
-25
lines changed

2 files changed

+49
-25
lines changed

db/btree.go

+48-24
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,23 @@ var (
8888
_ indexBtree = &indexInterior{}
8989
)
9090

91-
func newBtree(b []byte, isFileHeader bool) (interface{}, error) {
91+
func newBtree(b []byte, isFileHeader bool, pageSize int) (interface{}, error) {
9292
hb := b
9393
if isFileHeader {
9494
hb = b[headerSize:]
9595
}
9696
cells := int(binary.BigEndian.Uint16(hb[3:5]))
9797
switch typ := int(hb[0]); typ {
9898
case 0x0d:
99-
return newLeafTableBtree(cells, hb[8:], b)
99+
return newLeafTableBtree(cells, hb[8:], b, pageSize)
100100
case 0x05:
101101
rightmostPointer := int(binary.BigEndian.Uint32(hb[8:12]))
102102
return newInteriorTableBtree(cells, hb[12:], b, rightmostPointer)
103103
case 0x0a:
104-
return newLeafIndex(cells, b[8:], b)
104+
return newLeafIndex(cells, b[8:], b, pageSize)
105105
case 0x02:
106106
rightmostPointer := int(binary.BigEndian.Uint32(b[8:12]))
107-
return newInteriorIndex(cells, b[12:], b, rightmostPointer)
107+
return newInteriorIndex(cells, b[12:], b, rightmostPointer, pageSize)
108108
default:
109109
return nil, errors.New("unsupported page type")
110110
}
@@ -114,14 +114,15 @@ func newLeafTableBtree(
114114
count int,
115115
pointers []byte,
116116
content []byte,
117+
pageSize int,
117118
) (*tableLeaf, error) {
118119
cells, err := parseCellpointers(count, pointers, len(content))
119120
if err != nil {
120121
return nil, err
121122
}
122123
leafs := make([]tableLeafCell, len(cells))
123124
for i, start := range cells {
124-
leafs[i], err = parseTableLeaf(content[start:])
125+
leafs[i], err = parseTableLeaf(content[start:], pageSize)
125126
if err != nil {
126127
return nil, err
127128
}
@@ -250,14 +251,15 @@ func newLeafIndex(
250251
count int,
251252
pointers []byte,
252253
content []byte,
254+
pageSize int,
253255
) (*indexLeaf, error) {
254256
cells, err := parseCellpointers(count, pointers, len(content))
255257
if err != nil {
256258
return nil, err
257259
}
258260
cs := make([]cellPayload, len(cells))
259261
for i, start := range cells {
260-
cs[i], err = parseIndexLeaf(content[start:])
262+
cs[i], err = parseIndexLeaf(content[start:], pageSize)
261263
if err != nil {
262264
return nil, err
263265
}
@@ -323,14 +325,15 @@ func newInteriorIndex(
323325
pointers []byte,
324326
content []byte,
325327
rightmost int,
328+
pageSize int,
326329
) (*indexInterior, error) {
327330
cells, err := parseCellpointers(count, pointers, len(content))
328331
if err != nil {
329332
return nil, err
330333
}
331334
cs := make([]indexInteriorCell, len(cells))
332335
for i, start := range cells {
333-
cs[i], err = parseIndexInterior(content[start:])
336+
cs[i], err = parseIndexInterior(content[start:], pageSize)
334337
if err != nil {
335338
return nil, err
336339
}
@@ -458,28 +461,48 @@ func (l *indexInterior) Count(db *Database) (int, error) {
458461
return total + n, err
459462
}
460463

464+
func calculateCellInPageBytes(l int64, pageSize int, maxInPagePayload int) int {
465+
// Overflow calculation described in the file format spec. The
466+
// variable names and magic constants are from the spec exactly.
467+
u := int64(pageSize)
468+
p := l
469+
x := int64(maxInPagePayload)
470+
m := ((u - 12) * 32 / 255) - 23
471+
k := m + ((p - m) % (u - 4))
472+
473+
if p <= x {
474+
return int(l)
475+
} else if k <= x {
476+
return int(k)
477+
} else {
478+
return int(m)
479+
}
480+
}
481+
461482
// shared code for parsing payload from cells
462-
func parsePayload(l int64, c []byte) (cellPayload, error) {
483+
func parsePayload(l int64, c []byte, pageSize int, maxInPagePayload int) (cellPayload, error) {
463484
overflow := 0
485+
inPageBytes := calculateCellInPageBytes(l, pageSize, maxInPagePayload)
464486
if l < 0 {
465487
return cellPayload{}, ErrCorrupted
466488
}
467-
if int64(len(c)) > l {
468-
c = c[:l]
489+
490+
if int64(inPageBytes) == l {
491+
return cellPayload{l, c, 0}, nil
469492
}
470-
if int64(len(c)) != l {
471-
if len(c) < 4 {
472-
return cellPayload{}, ErrCorrupted
473-
}
474-
c, overflow = c[:len(c)-4], int(binary.BigEndian.Uint32(c[len(c)-4:]))
475-
if overflow == 0 {
476-
return cellPayload{}, ErrCorrupted
477-
}
493+
494+
if len(c) < inPageBytes+4 {
495+
return cellPayload{}, ErrCorrupted
496+
}
497+
498+
c, overflow = c[:inPageBytes], int(binary.BigEndian.Uint32(c[inPageBytes:inPageBytes+4]))
499+
if overflow == 0 {
500+
return cellPayload{}, ErrCorrupted
478501
}
479502
return cellPayload{l, c, overflow}, nil
480503
}
481504

482-
func parseTableLeaf(c []byte) (tableLeafCell, error) {
505+
func parseTableLeaf(c []byte, pageSize int) (tableLeafCell, error) {
483506
l, n := readVarint(c)
484507
if n < 0 {
485508
return tableLeafCell{}, ErrCorrupted
@@ -489,7 +512,8 @@ func parseTableLeaf(c []byte) (tableLeafCell, error) {
489512
if n < 0 {
490513
return tableLeafCell{}, ErrCorrupted
491514
}
492-
pl, err := parsePayload(l, c[n:])
515+
516+
pl, err := parsePayload(l, c[n:], pageSize, pageSize-35)
493517
return tableLeafCell{
494518
left: rowid,
495519
payload: pl,
@@ -511,15 +535,15 @@ func parseTableInterior(c []byte) (tableInteriorCell, error) {
511535
}, nil
512536
}
513537

514-
func parseIndexLeaf(c []byte) (cellPayload, error) {
538+
func parseIndexLeaf(c []byte, pageSize int) (cellPayload, error) {
515539
l, n := readVarint(c)
516540
if n < 0 {
517541
return cellPayload{}, ErrCorrupted
518542
}
519-
return parsePayload(l, c[n:])
543+
return parsePayload(l, c[n:], pageSize, ((pageSize-12)*64/255)-23)
520544
}
521545

522-
func parseIndexInterior(c []byte) (indexInteriorCell, error) {
546+
func parseIndexInterior(c []byte, pageSize int) (indexInteriorCell, error) {
523547
if len(c) < 4 {
524548
return indexInteriorCell{}, ErrCorrupted
525549
}
@@ -529,7 +553,7 @@ func parseIndexInterior(c []byte) (indexInteriorCell, error) {
529553
if n < 0 {
530554
return indexInteriorCell{}, ErrCorrupted
531555
}
532-
pl, err := parsePayload(l, c[n:])
556+
pl, err := parsePayload(l, c[n:], pageSize, ((pageSize-12)*64/255)-23)
533557
return indexInteriorCell{
534558
left: int(left),
535559
payload: pl,

db/database.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func (db *Database) openPage(page int) (interface{}, error) {
351351
if err != nil {
352352
return nil, err
353353
}
354-
p, err := newBtree(buf, page == 1)
354+
p, err := newBtree(buf, page == 1, db.header.PageSize)
355355
if err == nil {
356356
db.btreeCache.set(page, p)
357357
}

0 commit comments

Comments
 (0)