blob: e641f3795b1039b78ded72a72f43f3b65b98183f [file] [log] [blame]
// Copyright 2016 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package upspin
import (
"fmt"
"reflect"
"strings"
"testing"
)
var dirEnt = DirEntry{
Name: "u@foo.com/a/directory",
SignedName: "u@foo.com/a/directory",
Packing: EEPack,
Time: 123456,
Blocks: []DirBlock{
dirBlock1,
dirBlock2,
},
Link: "",
Packdata: []byte{1, 2, 3, 4},
Attr: AttrDirectory, // Just so it's not zero; this is not a semantically valid entry.
Sequence: 1234,
Writer: "u@foo.com",
}
var dirBlock1 = DirBlock{
Location: Location{
Endpoint: Endpoint{
Transport: Remote,
NetAddr: "foo.com:1234",
},
Reference: "Cinder",
},
Offset: 0,
Size: 1024,
Packdata: []byte("sign"),
}
var dirBlock2 = DirBlock{
Location: Location{
Endpoint: Endpoint{
Transport: InProcess,
NetAddr: "foo.com:1234",
},
Reference: "Ice",
},
Offset: 1024,
Size: 4096,
Packdata: []byte("nature"),
}
var linkDirEnt = DirEntry{
Name: "u@foo.com/a/link",
SignedName: "u@foo.com/a/link",
Link: "v@bar.com/b/foo",
Packing: PlainPack,
Time: 123456,
Packdata: nil, // Links have no packdata.
Attr: AttrLink,
Sequence: 1234,
Writer: "u@foo.com",
}
var nameCombinations = []DirEntry{
// These are nonsense entries, with many empty fields, but it's just a test.
{Name: "", SignedName: ""},
{Name: "user@example.com/path", SignedName: ""},
{Name: "", SignedName: "user@example.com/path"},
{Name: "user@example.com/path", SignedName: "user@example.com/path"},
{Name: "user@example.com/path", SignedName: "user@example.com/different/path"},
}
func TestDirEntryMarshal(t *testing.T) {
testDirEntryMarshal(t, "regular file", &dirEnt)
testDirEntryMarshal(t, "link", &linkDirEnt)
for _, e := range nameCombinations {
testDirEntryMarshal(t, fmt.Sprintf("Name=%q SignedName=%q", e.Name, e.SignedName), &e)
}
}
func testDirEntryMarshal(t *testing.T, msg string, entry *DirEntry) {
data, err := entry.Marshal()
if err != nil {
t.Fatalf("Marshal %s: %v", msg, err)
}
var new DirEntry
remaining, err := new.Unmarshal(data)
if err != nil {
t.Fatalf("Unmarshal %s: %v", msg, err)
}
if len(remaining) != 0 {
t.Errorf("%s: data remains after unmarshal", msg)
}
if !reflect.DeepEqual(entry, &new) {
t.Errorf("%s: bad result. got:\n\t%+v\nwant:\n\t%+v", msg, &new, entry)
}
}
func TestDirEntryMarshalClearsPackdata(t *testing.T) {
src, dst := linkDirEnt, dirEnt
data, err := src.Marshal()
if err != nil {
t.Fatal(err)
}
if _, err := dst.Unmarshal(data); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(src, dst) {
t.Errorf("bad result. got:\n\t%+v\nwant:\n\t%+v", dst, src)
}
}
// Was a bug that Unmarshal would not clear the Block field of the receiver
// if the unmarshaled entry was of zero length.
func TestDirEntryMarshalClearsBlocks(t *testing.T) {
full := dirEnt
empty := dirEnt
empty.Blocks = nil
data, err := empty.Marshal()
if err != nil {
t.Fatal(err)
}
remaining, err := full.Unmarshal(data)
if err != nil {
t.Fatal(err)
}
if len(remaining) != 0 {
t.Error("data remains after unmarshal")
}
if !reflect.DeepEqual(&empty, &full) {
t.Errorf("bad result. expected:")
t.Errorf("%+v\n", empty)
t.Errorf("got:")
t.Errorf("%+v\n", full)
}
}
func TestDirBlockMarshal(t *testing.T) {
data, err := dirBlock1.Marshal()
if err != nil {
t.Fatalf("Marshal: %v", err)
}
var new DirBlock
remaining, err := new.Unmarshal(data)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if len(remaining) != 0 {
t.Errorf("data remains after unmarshal")
}
if !reflect.DeepEqual(&dirBlock1, &new) {
t.Errorf("bad result. expected:")
t.Errorf("%+v\n", &dirBlock1)
t.Errorf("got:")
t.Errorf("%+v\n", &new)
}
}
func TestDirEntMarshalAppendNoMalloc(t *testing.T) {
// Marshal to see what length we need.
data, err := dirEnt.Marshal()
if err != nil {
t.Fatalf("Marshal: %v", err)
}
// Toss old data but keep length.
data = make([]byte, len(data))
p := &data[0]
data, err = dirEnt.MarshalAppend(data[:0])
if err != nil {
t.Fatalf("Marshal: %v", err)
}
if p != &data[0] {
t.Fatalf("MarshalAppend allocated")
}
}
func TestDirEntUnmarshalNoPanic(t *testing.T) {
_, err := dirEnt.Unmarshal([]byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2})
if err != ErrTooShort {
t.Fatal(err)
}
}
// Test inputs that caused crashes with go-fuzz.
func TestDirEntryUnmarshalCrashers(t *testing.T) {
inputs := []string{
"*0000000000000000000" +
"0000\x00\b0000\x00\x1200000000" +
"0\x13",
"*u@foo.co\xbdm/a/direct" +
"ory\x14\x80�\x04��",
"\x0000\x80�\x04",
"*0000000000000000000" +
"0000\x90�@",
"*@foo.com/a/director" +
"y\x14\x80\x89\x0e\xfe�\x16~��$\xef\xbf" +
"\xbd\x12\x02\x18foo.com:1234\fCin" +
"{er\x00\x80\x80\xae\xbfソソソソ" +
"\xef",
"*0000000000000000000" +
"000000\x18000000000000\x06" +
"000���0",
"\x000\xbd\xbd\xbfソ\u07fd\xbf0",
}
for _, s := range inputs {
var de DirEntry
de.Unmarshal([]byte(s))
}
}
// Tests of the buffer overflow code. These know about the structure of
// the code, but otherwise we'd be playing with 2GB buffers.
func TestMarshalBigDirBlock(t *testing.T) {
defer func() { maxInt32 = 1<<31 - 1 }()
maxInt32 = 1<<15 - 1 // Has been tested on a 64-bit machine at 1<<28-1.
d := DirBlock{
Location: Location{
Endpoint: Endpoint{
Transport: Remote,
NetAddr: "upspin.io",
},
Reference: "foo",
},
Offset: 0,
Size: 0,
Packdata: []byte{},
}
// Should succeed.
_, err := d.MarshalAppend(nil)
if err != nil {
t.Error("Marshal failed: ", err)
}
// Big Size should fail.
d.Size = MaxBlockSize + 1
_, err = d.MarshalAppend(nil)
if err != ErrTooLarge {
t.Error("Marshal big Size should fail with ErrTooLarge; got: ", err)
}
d.Size = 0
// Big Packdata should fail.
big := make([]byte, maxInt32+1)
d.Packdata = big
_, err = d.MarshalAppend(nil)
if err != ErrTooLarge {
t.Error("Marshal big Packdata should fail with ErrTooLarge; got: ", err)
}
// Every field OK but doesn't quite fit.
d.Packdata = big[:len(big)-10]
_, err = d.MarshalAppend(nil)
if err != ErrTooLarge {
t.Error("Marshal big Packdata should fail with ErrTooLarge; got: ", err)
}
// A little smaller should be OK.
// Start with enough headroom to discover the boundary.
d.Packdata = big[:len(big)-64]
b, err := d.MarshalAppend(nil)
if err != nil {
t.Error("Marshal just right Packdata should not fail; got: ", err)
}
startLen := uint64(len(b) - len(d.Packdata)) // It's 20, but let's compute it.
// Now we can get just to the edge.
d.Packdata = big[:maxInt32-startLen]
_, err = d.MarshalAppend(nil)
if err != nil {
t.Error("Marshal just right Packdata should not fail; got: ", err)
}
// One more byte should fail.
d.Packdata = big[:maxInt32-startLen+1]
_, err = d.MarshalAppend(nil)
if err != ErrTooLarge {
t.Error("Marshal just too big Packdata should fail with ErrTooLarge; got: ", err)
}
}
func TestMarshalBigDirEntry(t *testing.T) {
if testing.Short() {
t.Skip("Allocates too much for a short run.")
}
defer func() { maxInt32 = 1<<31 - 1 }()
maxInt32 = 1<<15 - 1 // Has been tested on a 64-bit machine at 1<<28-1.
de := dirEnt
// Should succeed.
_, err := de.MarshalAppend(nil)
if err != nil {
t.Error("Marshal failed: ", err)
}
// Too many blocks should fail.
manyBlocks := make([]DirBlock, maxInt32+1)
de.Blocks = manyBlocks
_, err = de.MarshalAppend(nil)
if err != ErrTooLarge {
t.Error("Marshal too many blocks should fail with ErrTooLarge; got: ", err)
}
// Too much data should fail. Approach the actual size in steps.
for size := 1000; ; size *= 2 {
de.Blocks = manyBlocks[:size] // Might as well re-use it.
for i := range de.Blocks {
de.Blocks[i] = dirBlock1
}
b, err := de.MarshalAppend(nil)
if err == nil {
if uint64(len(b)) > maxInt32 {
t.Fatalf("should have failed at size %d", len(b))
}
continue
}
if err != ErrTooLarge {
t.Fatal("unexpected error", err)
}
break
}
}
func TestUnmarshalBigDirBlock(t *testing.T) {
d := DirBlock{
Location: Location{
Endpoint: Endpoint{
Transport: Remote,
NetAddr: "upspin.io",
},
Reference: "foo",
},
Offset: 0,
Size: 0,
Packdata: make([]byte, 1<<15), // Small enough now, not later.
}
// Should succeed.
data, err := d.MarshalAppend(nil)
if err != nil {
t.Error("Marshal failed: ", err)
}
var db DirBlock
b, err := db.Unmarshal(data)
if err != nil {
t.Error("Unmarshal failed: ", err)
}
if len(b) != 0 {
t.Error("leftover data after Unmarshal")
}
if !reflect.DeepEqual(db, d) {
t.Error("data incorrect after Unmarshal")
t.Errorf("input: %#v", d)
t.Errorf("output: %#v", db)
}
// We now know that data correctly unmarshals.
// Change maxInt32 and we should fail.
defer func() { maxInt32 = 1<<31 - 1 }()
maxInt32 = 1<<15 - 1
_, err = d.Unmarshal(data)
if err != ErrTooLarge {
t.Errorf("Unmarshal got %v; want ErrTooLarge", err)
}
}
func TestUnmarshalBigDirEntry(t *testing.T) {
if testing.Short() {
t.Skip("Allocates too much for a short run.")
}
d := dirEnt
// We probed Packdata in TestUnmarshalBigDirBlock
// so we do something else here.
d.Link = PathName(strings.Repeat("x", 1<<15))
// Should succeed for now.
data, err := d.MarshalAppend(nil)
if err != nil {
t.Error("Marshal failed: ", err)
}
var de DirEntry
b, err := de.Unmarshal(data)
if err != nil {
t.Error("Unmarshal failed: ", err)
}
if len(b) != 0 {
t.Error("leftover data after Unmarshal")
}
if !reflect.DeepEqual(de, d) {
t.Error("data incorrect after Unmarshal")
t.Errorf("input: %#v", d)
t.Errorf("output: %#v", de)
}
// We now know that data correctly unmarshals.
// Change maxInt32 and we should fail.
defer func() { maxInt32 = 1<<31 - 1 }()
maxInt32 = 1<<15 - 1 // Has been tested on a 64-bit machine at 1<<28-1.
_, err = d.Unmarshal(data)
if err != ErrTooLarge {
t.Errorf("Unmarshal got %v; want ErrTooLarge", err)
}
}