Files

354 lines
9.9 KiB
Go

package main
import (
"path/filepath"
"testing"
"go.etcd.io/bbolt"
)
func newTestDB(t *testing.T, name string) *bbolt.DB {
t.Helper()
db, err := OpenDatabase(filepath.Join(t.TempDir(), name))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
db.Close()
})
return db
}
func TestResolveProgramUsesExactSignatureAndHints(t *testing.T) {
db := newTestDB(t, "match.db")
section := &SectionHash{Size: 0x30, Paddr: 0x4000, Digest: []byte{0xaa, 0xbb}}
sectionKey := SectionHashToKey(section, "elf", "linux")
if err := DbSetHintsMeta(db, sectionKey, "psk", ".text", "bin-match", []*Hint{{Bits: 64, Offset: 0x20}}); err != nil {
t.Fatal(err)
}
sig := &Signature{Arch: "x86", Bits: 64, Length: 16, Digest: []byte{0x01, 0x02}}
if err := DbSetSymbolMeta(db, SignatureToKey(sig), "psk", "bin-match", &Symbol{Name: "sym.good", Signature: "int good()", Callconv: "sysv", Bits: 64}, 0); err != nil {
t.Fatal(err)
}
server := &Server{
auths: map[string]bool{"psk": true},
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
shared: map[string]*bbolt.DB{},
}
result := server.ResolveProgram("psk", ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x30, Paddr: 0x4000, Digest: []byte{0xaa, 0xbb}},
},
Functions: []FunctionRecord{
{Addr: 0x4020, Bits: 64, Arch: "x86", Length: 16, Digest: []byte{0x01, 0x02}},
},
})
if len(result.Hints) != 1 || result.Hints[0].Offset != 0x4020 {
t.Fatalf("unexpected hints: %#v", result.Hints)
}
if len(result.Symbols) != 1 {
t.Fatalf("unexpected symbols: %#v", result.Symbols)
}
if !result.Symbols[0].Exact || result.Symbols[0].Symbol.Name != "sym.good" {
t.Fatalf("exact symbol mismatch: %#v", result.Symbols[0])
}
if result.Symbols[0].MatchedBy != "exact_signature" {
t.Fatalf("unexpected match type: %#v", result.Symbols[0])
}
}
func TestShareProgramStoresExactDataWithBinaryID(t *testing.T) {
shareDB := newTestDB(t, "share.db")
server := &Server{
auths: map[string]bool{"psk": true},
search: map[string][]*bbolt.DB{GENERIC_DB: {shareDB}},
shared: map[string]*bbolt.DB{"psk": shareDB},
}
bundle := ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x40, Paddr: 0x1000, Digest: []byte{0xde, 0xad}},
},
Functions: []FunctionRecord{
{
Addr: 0x1010,
Size: 16,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
Name: "sym.main",
Signature: "int main()",
Callconv: "sysv",
},
},
}
result := server.ShareProgram("psk", bundle)
if result.BinaryID == "" {
t.Fatal("share result binary id is empty")
}
normalized := normalizeProgramBundle(bundle)
sectionMeta, err := DbGetHintsMeta(shareDB, SectionHashToKey(&SectionHash{
Size: normalized.Sections[0].Size,
Paddr: normalized.Sections[0].Paddr,
Digest: normalized.Sections[0].Digest,
}, normalized.BinaryType, normalized.OS))
if err != nil {
t.Fatal(err)
}
if sectionMeta == nil || sectionMeta.BinaryID != result.BinaryID || len(sectionMeta.Hints) != 1 || sectionMeta.Hints[0].Offset != 0x10 {
t.Fatalf("unexpected section meta: %#v", sectionMeta)
}
sig := functionSignature(normalized, normalized.Functions[0])
symbolMeta, err := DbGetSymbolMeta(shareDB, SignatureToKey(sig))
if err != nil {
t.Fatal(err)
}
if symbolMeta == nil || symbolMeta.BinaryID != result.BinaryID || symbolMeta.Symbol == nil || symbolMeta.Symbol.Name != "sym.main" {
t.Fatalf("unexpected symbol meta: %#v", symbolMeta)
}
locationMeta, err := DbGetLocationSymbolMeta(shareDB, FunctionLocationToKey(result.BinaryID, normalized.Functions[0].SectionName, normalized.Functions[0].SectionPaddr, normalized.Functions[0].SectionOffset))
if err != nil {
t.Fatal(err)
}
if locationMeta == nil || locationMeta.Symbol == nil || locationMeta.Symbol.Name != "sym.main" {
t.Fatalf("unexpected location symbol meta: %#v", locationMeta)
}
if locationMeta.Size != 16 {
t.Fatalf("unexpected location symbol size: %#v", locationMeta)
}
}
func TestShareProgramPreservesVASectionOffsets(t *testing.T) {
shareDB := newTestDB(t, "share-va.db")
server := &Server{
auths: map[string]bool{"psk": true},
search: map[string][]*bbolt.DB{GENERIC_DB: {shareDB}},
shared: map[string]*bbolt.DB{"psk": shareDB},
}
bundle := ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x40, Paddr: 0x1000, Digest: []byte{0xde, 0xad}},
},
Functions: []FunctionRecord{
{
Addr: 0x401010,
Size: 16,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
Name: "sym.main",
},
},
}
server.ShareProgram("psk", bundle)
normalized := normalizeProgramBundle(bundle)
sectionMeta, err := DbGetHintsMeta(shareDB, SectionHashToKey(&SectionHash{
Size: normalized.Sections[0].Size,
Paddr: normalized.Sections[0].Paddr,
Digest: normalized.Sections[0].Digest,
}, normalized.BinaryType, normalized.OS))
if err != nil {
t.Fatal(err)
}
if sectionMeta == nil || len(sectionMeta.Hints) != 1 || sectionMeta.Hints[0].Offset != 0x10 {
t.Fatalf("unexpected section meta: %#v", sectionMeta)
}
}
func TestResolveProgramPrefersExactLocationOverAmbiguousSignature(t *testing.T) {
db := newTestDB(t, "location.db")
server := &Server{
auths: map[string]bool{"psk": true},
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
shared: map[string]*bbolt.DB{"psk": db},
}
bundle := normalizeProgramBundle(ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
},
Functions: []FunctionRecord{
{
Addr: 0x1010,
Size: 16,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
Name: "sym.real_name",
},
},
})
share := server.ShareProgram("psk", bundle)
if share.BinaryID == "" {
t.Fatal("expected binary id")
}
sig := functionSignature(bundle, bundle.Functions[0])
if err := DbSetSymbolMeta(db, SignatureToKey(sig), "psk", share.BinaryID, &Symbol{Name: "sym.wrong_name"}, 0); err != nil {
t.Fatal(err)
}
result := server.ResolveProgram("psk", ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
},
Functions: []FunctionRecord{
{
Addr: 0x401010,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
},
},
})
if len(result.Symbols) != 1 {
t.Fatalf("unexpected symbols: %#v", result.Symbols)
}
if got := result.Symbols[0].Symbol.Name; got != "sym.real_name" {
t.Fatalf("expected location symbol, got %q", got)
}
if got := result.Symbols[0].MatchedBy; got != "exact_location" {
t.Fatalf("expected exact_location match, got %q", got)
}
if got := result.Symbols[0].Offset; got != 0x1010 {
t.Fatalf("expected exact offset, got %#x", got)
}
if got := result.Symbols[0].Size; got != 16 {
t.Fatalf("expected exact size, got %d", got)
}
}
func TestResolveProgramReturnsExtraExactLocationSymbolsForHintedOffsets(t *testing.T) {
db := newTestDB(t, "hinted-location.db")
server := &Server{
auths: map[string]bool{"psk": true},
search: map[string][]*bbolt.DB{GENERIC_DB: {db}},
shared: map[string]*bbolt.DB{"psk": db},
}
bundle := normalizeProgramBundle(ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
},
Functions: []FunctionRecord{
{
Addr: 0x1010,
Size: 16,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
Name: "sym.main",
},
{
Addr: 0x1020,
Size: 8,
Bits: 64,
Arch: "x86",
Length: 8,
Digest: []byte{0xca, 0xff},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x20,
Name: "sym.cold",
},
},
})
share := server.ShareProgram("psk", bundle)
if share.BinaryID == "" {
t.Fatal("expected binary id")
}
result := server.ResolveProgram("psk", ProgramBundle{
BinaryType: "elf",
OS: "linux",
Arch: "x86",
Bits: 64,
Sections: []SectionRecord{
{Name: ".text", Size: 0x80, Paddr: 0x1000, Digest: []byte{0xaa, 0xbb}},
},
Functions: []FunctionRecord{
{
Addr: 0x401010,
Bits: 64,
Arch: "x86",
Length: 16,
Digest: []byte{0xca, 0xfe},
SectionName: ".text",
SectionPaddr: 0x1000,
SectionOffset: 0x10,
},
},
})
if len(result.Symbols) != 2 {
t.Fatalf("unexpected symbols: %#v", result.Symbols)
}
if got := result.Symbols[1].Symbol.Name; got != "sym.cold" {
t.Fatalf("expected hinted exact location symbol, got %q", got)
}
if got := result.Symbols[1].MatchedBy; got != "exact_hint_location" {
t.Fatalf("expected exact_hint_location match, got %q", got)
}
if got := result.Symbols[1].Offset; got != 0x1020 {
t.Fatalf("expected hinted offset, got %#x", got)
}
if got := result.Symbols[1].Size; got != 8 {
t.Fatalf("expected hinted size, got %d", got)
}
}