diff options
Diffstat (limited to 'prog/kfuzztest_test.go')
| -rw-r--r-- | prog/kfuzztest_test.go | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/prog/kfuzztest_test.go b/prog/kfuzztest_test.go new file mode 100644 index 000000000..5f1b3d9c4 --- /dev/null +++ b/prog/kfuzztest_test.go @@ -0,0 +1,262 @@ +// Copyright 2025 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. +package prog + +import ( + "bytes" + "encoding/binary" + "testing" + + "github.com/stretchr/testify/assert" +) + +type testCase struct { + prog string + extractArg func(*Prog) Arg + regionArray []any + relocationTable []any + payload []any +} + +func TestRoundUpPowerOfTwo(t *testing.T) { + if res := roundUpPowerOfTwo(9, 8); res != 16 { + t.Fatalf("expected 16, got %d", res) + } + if res := roundUpPowerOfTwo(21, 4); res != 24 { + t.Fatalf("expected 24, got %d", res) + } + if res := roundUpPowerOfTwo(113, 16); res != 128 { + t.Fatalf("expected 24, got %d", res) + } +} + +func createBuffer(data []any) []byte { + var buf bytes.Buffer + + for _, d := range data { + switch val := d.(type) { + case uint8, uint16, uint32, uint64: + binary.Write(&buf, binary.LittleEndian, val) + case []byte: + buf.Write(val) + } + } + + return buf.Bytes() +} + +func createPrefix() []byte { + var prefix bytes.Buffer + binary.Write(&prefix, binary.LittleEndian, kFuzzTestMagic) + binary.Write(&prefix, binary.LittleEndian, uint32(0)) + return prefix.Bytes() +} + +//nolint:all +func TestMarshallKFuzzTestArg(t *testing.T) { + testCases := []testCase{ + // This test case validates the encoding of the following structure: + // msg: ptr[in, msghdr_netlink[netlink_msg_xfrm]] { + // msghdr_netlink[netlink_msg_xfrm] { + // addr: nil + // addrlen: len = 0x0 (4 bytes) + // pad = 0x0 (4 bytes) + // vec: ptr[in, iovec[in, netlink_msg_xfrm]] { + // iovec[in, netlink_msg_xfrm] { + // addr: ptr[inout, array[ANYUNION]] { + // array[ANYUNION] { + // } + // } + // len: len = 0x33fe0 (8 bytes) + // } + // } + // vlen: const = 0x1 (8 bytes) + // ctrl: const = 0x0 (8 bytes) + // ctrllen: const = 0x0 (8 bytes) + // f: send_flags = 0x0 (4 bytes) + // pad = 0x0 (4 bytes) + // } + // } + { + `r0 = openat$cgroup_ro(0xffffffffffffff9c, &(0x7f00000003c0)='cpuacct.stat\x00', 0x26e1, 0x0) +sendmsg$nl_xfrm(r0, &(0x7f0000000240)={0x0, 0x0, &(0x7f0000000080)={&(0x7f00000001c0)=ANY=[], 0x33fe0}}, 0x0)`, + func(p *Prog) Arg { + sendMsgCall := p.Calls[1] + msgHdr := sendMsgCall.Args[1].(*PointerArg).Res + return msgHdr + }, + []any{ + uint32(3), // Num regions. + + // Region definitions: (offset, size) pairs. + uint32(0), uint32(0x38), + uint32(0x40), uint32(0x10), + uint32(0x58), uint32(0x0), + }, + []any{ + uint32(3), // Num entries. + uint32(0x8), // Bytes of padding. + + // Relocation definitions: (source region, offset, dest region) triplets. + uint32(0), uint32(0x00), kFuzzTestRegionIDNull, + uint32(0), uint32(0x10), uint32(1), + uint32(1), uint32(0x00), uint32(2), + uint64(0x0), // 8 bytes of padding. + }, + []any{ + // Region 0 data. + kFuzzTestPlaceHolderPtr, // `addr` field, placeholder pointer. + uint32(0x0), // `addrlen`. + uint32(0x0), // `pad[4]`. + kFuzzTestPlaceHolderPtr, // `vec` field, placeholder pointer. + uint64(0x1), // `vlen`. + uint64(0x0), // `ctrl`. + uint64(0x0), // `ctrllen`. + uint32(0x0), // `f`. + uint32(0x0), // `pad[4]`. + + uint64(0x0), // 8 bytes of padding between regions. + + // Region 1 data. + kFuzzTestPlaceHolderPtr, // `addr` field, placeholder pointer. + uint64(0x033fe0), // `len`. + + make([]byte, kFuzzTestPoisonSize), // Inter-region padding. + + []byte{}, // Region 2 data (empty). + + make([]byte, kFuzzTestPoisonSize), // Tail padding. + }, + }, + // This test case validates the encoding of the following structure: + // loop_info64 { + // lo_device: const = 0x0 (8 bytes) + // lo_inode: const = 0x0 (8 bytes) + // lo_rdevice: const = 0x0 (8 bytes) + // lo_offset: int64 = 0x1 (8 bytes) + // lo_sizelimit: int64 = 0x8005 (8 bytes) + // lo_number: const = 0x0 (4 bytes) + // lo_enc_type: lo_encrypt_type = 0x0 (4 bytes) + // lo_enc_key_size: int32 = 0x19 (4 bytes) + // lo_flags: lo_flags = 0x1c (4 bytes) + // lo_file_name: buffer: {ef 35 9f 41 3b b9 38 52 f7 d6 a4 ae 6d dd fb + // d1 ce 5d 29 c2 ee 5e 5c a9 00 0f f8 ee 09 e7 37 ff 0e df 11 0f f4 11 + // 76 39 c2 eb 4b 78 c6 60 e6 77 df 70 19 05 b9 aa fa b4 af aa f7 55 a3 + // f6 a0 04} (length 0x40) lo_crypt_name: buffer: {03 6c 47 c6 78 08 20 + // d1 cb f7 96 6d 61 fd cf 33 52 63 bd 9b ff bc c2 54 2d ed 71 03 82 59 + // ca 17 1c e1 a3 11 ef 54 ec 32 d7 1e 14 ef 3d c1 77 e9 b4 8b 00 00 00 + // 00 00 00 00 00 00 00 00 00 00 00} (length 0x40) lo_enc_key: buffer: + // {f2 83 59 73 8e 22 9a 4c 66 81 00 00 00 00 00 d3 00 e6 d6 02 00 00 + // 00 00 00 00 00 00 00 00 00 01} (length 0x20) lo_init: array[int64] { + // int64 = 0x204 (8 bytes) + // int64 = 0x0 (8 bytes) + // } + // } + // } + // ] + { + `r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0) +ioctl$LOOP_SET_STATUS64(r0, 0x4c04, &(0x7f0000000540)={0x0, 0x0, 0x0, 0x1, 0x8005, 0x0, 0x0, 0x19, 0x1c, "ef359f413bb93852f7d6a4ae6dddfbd1ce5d29c2ee5e5ca9000ff8ee09e737ff0edf110ff4117639c2eb4b78c660e677df701905b9aafab4afaaf755a3f6a004", "036c47c6780820d1cbf7966d61fdcf335263bd9bffbcc2542ded71038259ca171ce1a311ef54ec32d71e14ef3dc177e9b48b00", "f28359738e229a4c66810000000000d300e6d602000000000000000000000001", [0x204]})`, + func(p *Prog) Arg { + ioctlCall := p.Calls[1] + ptrArg := ioctlCall.Args[2].(*PointerArg) + ret := ptrArg.Res + return ret + }, + []any{ + uint32(1), // Num regions. + + // Region definitions: (offset, size) pairs. + uint32(0), uint32(0xe8), + }, + []any{ + uint32(0), // Num entries. + uint32(12), // Number of bytes of padding. + make([]byte, 12), // Padding. + }, + []any{ + uint64(0x0), // `lo_device`. + uint64(0x0), // `lo_inode`. + uint64(0x0), // `lo_rdevice`. + uint64(0x1), // `lo_offset`. + uint64(0x8005), // `lo_sizelimit`. + uint32(0x0), // `lo_number`. + uint32(0x0), // `lo_enc_type`. + uint32(0x19), // `lo_enc_key_size`. + uint32(0x1c), // `lo_flags`. + []byte{ + 0xef, 0x35, 0x9f, 0x41, 0x3b, 0xb9, 0x38, 0x52, + 0xf7, 0xd6, 0xa4, 0xae, 0x6d, 0xdd, 0xfb, 0xd1, + 0xce, 0x5d, 0x29, 0xc2, 0xee, 0x5e, 0x5c, 0xa9, + 0x00, 0x0f, 0xf8, 0xee, 0x09, 0xe7, 0x37, 0xff, + 0x0e, 0xdf, 0x11, 0x0f, 0xf4, 0x11, 0x76, 0x39, + 0xc2, 0xeb, 0x4b, 0x78, 0xc6, 0x60, 0xe6, 0x77, + 0xdf, 0x70, 0x19, 0x05, 0xb9, 0xaa, 0xfa, 0xb4, + 0xaf, 0xaa, 0xf7, 0x55, 0xa3, 0xf6, 0xa0, 0x04, + }, // `lo_file_name`. + []byte{ + 0x03, 0x6c, 0x47, 0xc6, 0x78, 0x08, 0x20, 0xd1, + 0xcb, 0xf7, 0x96, 0x6d, 0x61, 0xfd, 0xcf, 0x33, + 0x52, 0x63, 0xbd, 0x9b, 0xff, 0xbc, 0xc2, 0x54, + 0x2d, 0xed, 0x71, 0x03, 0x82, 0x59, 0xca, 0x17, + 0x1c, 0xe1, 0xa3, 0x11, 0xef, 0x54, 0xec, 0x32, + 0xd7, 0x1e, 0x14, 0xef, 0x3d, 0xc1, 0x77, 0xe9, + 0xb4, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, // `lo_crypt_name`. + []byte{ + 0xf2, 0x83, 0x59, 0x73, 0x8e, 0x22, 0x9a, 0x4c, + 0x66, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3, + 0x00, 0xe6, 0xd6, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, // `lo_enc_key`. + uint64(0x204), // `lo_init[0]. + uint64(0x0), // `lo_init[1]. + + make([]byte, kFuzzTestPoisonSize), // Tail padding. + }, + }, + } + + for _, tc := range testCases { + testOne(t, tc) + } +} + +func testOne(t *testing.T, tc testCase) { + target, err := GetTarget("linux", "amd64") + if err != nil { + t.Fatal(err) + } + p, err := target.Deserialize([]byte(tc.prog), NonStrict) + if err != nil { + t.Fatal(err) + } + + arg := tc.extractArg(p) + encoded := MarshallKFuzztestArg(arg) + + wantPrefix := createPrefix() + wantRegionArray := createBuffer(tc.regionArray) + wantRelocTable := createBuffer(tc.relocationTable) + wantPayload := createBuffer(tc.payload) + + regionArrayLen := len(wantRegionArray) + relocTableLen := len(wantRelocTable) + payloadLen := len(wantPayload) + + if len(encoded) != kFuzzTestPrefixSize+regionArrayLen+relocTableLen+payloadLen { + t.Fatalf("encoded output has wrong total length: got %d, want %d", + len(encoded), regionArrayLen+relocTableLen+payloadLen) + } + + gotPrefix := encoded[:kFuzzTestPrefixSize] + gotRegionArray := encoded[kFuzzTestPrefixSize : kFuzzTestPrefixSize+regionArrayLen] + gotRelocTable := encoded[kFuzzTestPrefixSize+regionArrayLen : kFuzzTestPrefixSize+regionArrayLen+relocTableLen] + gotPayload := encoded[kFuzzTestPrefixSize+regionArrayLen+relocTableLen:] + + assert.Equal(t, wantPrefix, gotPrefix) + assert.Equal(t, wantRegionArray, gotRegionArray) + assert.Equal(t, wantRelocTable, gotRelocTable) + assert.Equal(t, wantPayload, gotPayload) +} |
