Skip to content

Commit 9d196a3

Browse files
committed
Adding a timeout to fix infinite looping on corrupt bdb database files.
1 parent a8af76a commit 9d196a3

File tree

7 files changed

+45
-7
lines changed

7 files changed

+45
-7
lines changed

pkg/bdb/bdb.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package bdb
22

33
import (
4+
"context"
45
"io"
56
"os"
67

@@ -61,7 +62,7 @@ func (db *BerkeleyDB) Close() error {
6162
return db.file.Close()
6263
}
6364

64-
func (db *BerkeleyDB) Read() <-chan dbi.Entry {
65+
func (db *BerkeleyDB) Read(ctx context.Context) <-chan dbi.Entry {
6566
entries := make(chan dbi.Entry)
6667

6768
go func() {
@@ -118,6 +119,7 @@ func (db *BerkeleyDB) Read() <-chan dbi.Entry {
118119

119120
// Traverse the page to concatenate the data that may span multiple pages.
120121
valueContent, err := HashPageValueContent(
122+
ctx,
121123
db.file,
122124
pageData,
123125
hashPageIndex,

pkg/bdb/hash_page.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package bdb
22

33
import (
44
"bytes"
5+
"context"
56
"encoding/binary"
67
"io"
78
"os"
@@ -31,7 +32,7 @@ func ParseHashPage(data []byte, swapped bool) (*HashPage, error) {
3132
return &hashPage, nil
3233
}
3334

34-
func HashPageValueContent(db *os.File, pageData []byte, hashPageIndex uint16, pageSize uint32, swapped bool) ([]byte, error) {
35+
func HashPageValueContent(ctx context.Context, db *os.File, pageData []byte, hashPageIndex uint16, pageSize uint32, swapped bool) ([]byte, error) {
3536
// the first byte is the page type, so we can peek at it first before parsing further...
3637
valuePageType := pageData[hashPageIndex]
3738

@@ -50,6 +51,12 @@ func HashPageValueContent(db *os.File, pageData []byte, hashPageIndex uint16, pa
5051
var hashValue []byte
5152

5253
for currentPageNo := entry.PageNo; currentPageNo != 0; {
54+
select {
55+
case <-ctx.Done():
56+
return nil, xerrors.Errorf("timed out parsing hash page")
57+
default:
58+
}
59+
5360
pageStart := pageSize * currentPageNo
5461

5562
_, err := db.Seek(int64(pageStart), io.SeekStart)

pkg/db/rpmdbinterface.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package dbi
22

3+
import "context"
4+
35
type Entry struct {
46
Value []byte
57
Err error
68
}
79

810
type RpmDBInterface interface {
9-
Read() <-chan Entry
11+
Read(ctx context.Context) <-chan Entry
1012
Close() error
1113
}

pkg/ndb/ndb.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ SOFTWARE.
2222
package ndb
2323

2424
import (
25+
"context"
2526
"encoding/binary"
2627
"fmt"
2728
"io"
@@ -136,7 +137,7 @@ func (db *RpmNDB) Close() error {
136137
return db.file.Close()
137138
}
138139

139-
func (db *RpmNDB) Read() <-chan dbi.Entry {
140+
func (db *RpmNDB) Read(ctx context.Context) <-chan dbi.Entry {
140141
entries := make(chan dbi.Entry)
141142

142143
go func() {

pkg/rpmdb.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package rpmdb
22

33
import (
4+
"context"
5+
46
"github.com/knqyf263/go-rpmdb/pkg/bdb"
57
dbi "github.com/knqyf263/go-rpmdb/pkg/db"
68
"github.com/knqyf263/go-rpmdb/pkg/ndb"
@@ -47,7 +49,11 @@ func (d *RpmDB) Close() error {
4749
}
4850

4951
func (d *RpmDB) Package(name string) (*PackageInfo, error) {
50-
pkgs, err := d.ListPackages()
52+
return d.PackageWithContext(context.TODO(), name)
53+
}
54+
55+
func (d *RpmDB) PackageWithContext(ctx context.Context, name string) (*PackageInfo, error) {
56+
pkgs, err := d.ListPackagesWithContext(ctx)
5157
if err != nil {
5258
return nil, xerrors.Errorf("unable to list packages: %w", err)
5359
}
@@ -61,9 +67,13 @@ func (d *RpmDB) Package(name string) (*PackageInfo, error) {
6167
}
6268

6369
func (d *RpmDB) ListPackages() ([]*PackageInfo, error) {
70+
return d.ListPackagesWithContext(context.TODO())
71+
}
72+
73+
func (d *RpmDB) ListPackagesWithContext(ctx context.Context) ([]*PackageInfo, error) {
6474
var pkgList []*PackageInfo
6575

66-
for entry := range d.db.Read() {
76+
for entry := range d.db.Read(ctx) {
6777
if entry.Err != nil {
6878
return nil, entry.Err
6979
}

pkg/rpmdb_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package rpmdb
22

33
import (
4+
"context"
45
"os"
56
"testing"
7+
"time"
68

79
"github.com/stretchr/testify/assert"
810
"github.com/stretchr/testify/require"
@@ -824,3 +826,16 @@ func TestNevra(t *testing.T) {
824826
_, err = pkg.InstalledFiles()
825827
require.Error(t, err)
826828
}
829+
830+
func TestTimeoutPackages(t *testing.T) {
831+
db, err := Open("testdata/centos7-many/Packages")
832+
require.NoError(t, err)
833+
ctxTimesOut, cancelFunc := context.WithTimeout(context.Background(), 1*time.Microsecond)
834+
defer cancelFunc()
835+
_, err = db.ListPackagesWithContext(ctxTimesOut)
836+
if err == nil {
837+
t.Errorf("Expected timeout parsing hash page")
838+
} else {
839+
assert.Equal(t, "timed out parsing hash page", err.Error())
840+
}
841+
}

pkg/sqlite3/sqlite3.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sqlite3
22

33
import (
44
"bytes"
5+
"context"
56
"database/sql"
67
"encoding/binary"
78
"os"
@@ -44,7 +45,7 @@ func Open(path string) (*SQLite3, error) {
4445
return &SQLite3{db}, nil
4546
}
4647

47-
func (db *SQLite3) Read() <-chan dbi.Entry {
48+
func (db *SQLite3) Read(ctx context.Context) <-chan dbi.Entry {
4849
entries := make(chan dbi.Entry)
4950

5051
go func() {

0 commit comments

Comments
 (0)