blob: 95da225ce58662add43cf0f6c86d9ebcc9995a92 [file] [edit]
package sshfx
import (
"bytes"
"testing"
)
// packetBody returns the encoding of p without the uint32(length),
// which is what RawPacket.UnmarshalBinary and RequestPacket.UnmarshalBinary expect.
func packetBody(tb testing.TB, p PacketMarshaller) []byte {
tb.Helper()
data, err := ComposePacket(p.MarshalPacket(42, nil))
if err != nil {
tb.Fatal(err)
}
// The unmarshallers expect the packet type, but not ComposePacket's length prefix.
return data[4:]
}
var fuzzAttrs = Attributes{
Flags: AttrSize | AttrUIDGID | AttrPermissions | AttrACModTime | AttrExtended,
Size: 1 << 40,
UID: 1000,
GID: 100,
Permissions: ModeRegular | 0o644,
ATime: 1,
MTime: 2,
Extended: []ExtendedAttribute{{Type: "foo", Data: "bar"}},
}
// FuzzRequestPacket decodes request packets the way the server does,
// and checks that whatever decodes can be encoded and decoded again unchanged.
func FuzzRequestPacket(f *testing.F) {
packets := []PacketMarshaller{
&OpenPacket{Filename: "/foo", PFlags: FlagRead | FlagWrite | FlagCreate, Attrs: fuzzAttrs},
&WritePacket{Handle: "1", Offset: 1 << 40, Data: []byte("data")},
&ReadPacket{Handle: "1", Offset: 1 << 40, Length: 1 << 15},
&SetStatPacket{Path: "/foo", Attrs: fuzzAttrs},
&RenamePacket{OldPath: "/foo", NewPath: "/bar"},
&ReadDirPacket{Handle: "2"},
&ExtendedPacket{ExtendedRequest: "foo@example.com", Data: NewBuffer([]byte("data"))},
}
for _, p := range packets {
f.Add(packetBody(f, p))
}
f.Fuzz(func(t *testing.T, data []byte) {
var p RequestPacket
if err := p.UnmarshalBinary(data); err != nil {
return
}
first, err := p.MarshalBinary()
if err != nil {
t.Fatalf("MarshalBinary() of decoded %T: %v", p.Request, err)
}
var p2 RequestPacket
if err := p2.UnmarshalBinary(first[4:]); err != nil {
t.Fatalf("UnmarshalBinary() of re-encoded %T: %v\n%X", p.Request, err, first)
}
second, err := p2.MarshalBinary()
if err != nil {
t.Fatalf("MarshalBinary() of re-decoded %T: %v", p2.Request, err)
}
if !bytes.Equal(first, second) {
t.Fatalf("%T does not survive a round trip:\n first: %X\nsecond: %X", p.Request, first, second)
}
})
}
// FuzzPacketBody decodes packets of any type the way the client does for responses,
// and checks that whatever decodes can be encoded and decoded again unchanged.
func FuzzPacketBody(f *testing.F) {
packets := []PacketMarshaller{
&StatusPacket{StatusCode: StatusNoSuchFile, ErrorMessage: "no such file", LanguageTag: "en"},
&HandlePacket{Handle: "1"},
&DataPacket{Data: []byte("data")},
&NamePacket{Entries: []*NameEntry{
{Filename: "foo", Longname: "-rw-r--r-- 1 user group 3 Jan 1 00:00 foo", Attrs: fuzzAttrs},
{Filename: "bar"},
}},
&AttrsPacket{Attrs: fuzzAttrs},
&ExtendedReplyPacket{Data: NewBuffer([]byte("data"))},
}
for _, p := range packets {
f.Add(packetBody(f, p))
}
f.Fuzz(func(t *testing.T, data []byte) {
var raw RawPacket
if err := raw.UnmarshalBinary(data); err != nil {
return
}
body, err := raw.PacketBody()
if err != nil {
return
}
first, err := ComposePacket(body.MarshalPacket(raw.RequestID, nil))
if err != nil {
t.Fatalf("MarshalPacket() of decoded %T: %v", body, err)
}
var raw2 RawPacket
if err := raw2.UnmarshalBinary(first[4:]); err != nil {
t.Fatalf("UnmarshalBinary() of re-encoded %T: %v\n%X", body, err, first)
}
body2, err := raw2.PacketBody()
if err != nil {
t.Fatalf("PacketBody() of re-encoded %T: %v\n%X", body, err, first)
}
second, err := ComposePacket(body2.MarshalPacket(raw2.RequestID, nil))
if err != nil {
t.Fatalf("MarshalPacket() of re-decoded %T: %v", body2, err)
}
if !bytes.Equal(first, second) {
t.Fatalf("%T does not survive a round trip:\n first: %X\nsecond: %X", body, first, second)
}
})
}
// FuzzVersionPacket decodes the version packet the client receives during the handshake.
func FuzzVersionPacket(f *testing.F) {
packet := &VersionPacket{
Version: 3,
Extensions: []*ExtensionPair{
{Name: "posix-rename@openssh.com", Data: "1"},
{Name: "statvfs@openssh.com", Data: "2"},
},
}
seed, err := packet.MarshalBinary()
if err != nil {
f.Fatal(err)
}
// Skip the uint32(length) and uint8(type), as UnmarshalBinary expects.
f.Add(seed[5:])
f.Fuzz(func(t *testing.T, data []byte) {
var p VersionPacket
if err := p.UnmarshalBinary(data); err != nil {
return
}
first, err := p.MarshalBinary()
if err != nil {
t.Fatal("MarshalBinary():", err)
}
var p2 VersionPacket
if err := p2.UnmarshalBinary(first[5:]); err != nil {
t.Fatalf("UnmarshalBinary() of re-encoded packet: %v\n%X", err, first)
}
second, err := p2.MarshalBinary()
if err != nil {
t.Fatal("MarshalBinary():", err)
}
if !bytes.Equal(first, second) {
t.Fatalf("VersionPacket does not survive a round trip:\n first: %X\nsecond: %X", first, second)
}
})
}