github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/kfuzztest_test.go (about)

     1  // Copyright 2025 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  package prog
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  )
    12  
    13  type testCase struct {
    14  	prog            string
    15  	extractArg      func(*Prog) Arg
    16  	regionArray     []any
    17  	relocationTable []any
    18  	payload         []any
    19  }
    20  
    21  func TestRoundUpPowerOfTwo(t *testing.T) {
    22  	if res := roundUpPowerOfTwo(9, 8); res != 16 {
    23  		t.Fatalf("expected 16, got %d", res)
    24  	}
    25  	if res := roundUpPowerOfTwo(21, 4); res != 24 {
    26  		t.Fatalf("expected 24, got %d", res)
    27  	}
    28  	if res := roundUpPowerOfTwo(113, 16); res != 128 {
    29  		t.Fatalf("expected 24, got %d", res)
    30  	}
    31  }
    32  
    33  func createBuffer(data []any) []byte {
    34  	var buf bytes.Buffer
    35  
    36  	for _, d := range data {
    37  		switch val := d.(type) {
    38  		case uint8, uint16, uint32, uint64:
    39  			binary.Write(&buf, binary.LittleEndian, val)
    40  		case []byte:
    41  			buf.Write(val)
    42  		}
    43  	}
    44  
    45  	return buf.Bytes()
    46  }
    47  
    48  func createPrefix() []byte {
    49  	var prefix bytes.Buffer
    50  	binary.Write(&prefix, binary.LittleEndian, kFuzzTestMagic)
    51  	binary.Write(&prefix, binary.LittleEndian, uint32(0))
    52  	return prefix.Bytes()
    53  }
    54  
    55  //nolint:all
    56  func TestMarshallKFuzzTestArg(t *testing.T) {
    57  	testCases := []testCase{
    58  		// This test case validates the encoding of the following structure:
    59  		//    msg: ptr[in, msghdr_netlink[netlink_msg_xfrm]] {
    60  		//      msghdr_netlink[netlink_msg_xfrm] {
    61  		//        addr: nil
    62  		//        addrlen: len = 0x0 (4 bytes)
    63  		//        pad = 0x0 (4 bytes)
    64  		//        vec: ptr[in, iovec[in, netlink_msg_xfrm]] {
    65  		//          iovec[in, netlink_msg_xfrm] {
    66  		//            addr: ptr[inout, array[ANYUNION]] {
    67  		//              array[ANYUNION] {
    68  		//              }
    69  		//            }
    70  		//            len: len = 0x33fe0 (8 bytes)
    71  		//          }
    72  		//        }
    73  		//        vlen: const = 0x1 (8 bytes)
    74  		//        ctrl: const = 0x0 (8 bytes)
    75  		//        ctrllen: const = 0x0 (8 bytes)
    76  		//        f: send_flags = 0x0 (4 bytes)
    77  		//        pad = 0x0 (4 bytes)
    78  		//      }
    79  		//    }
    80  		{
    81  			`r0 = openat$cgroup_ro(0xffffffffffffff9c, &(0x7f00000003c0)='cpuacct.stat\x00', 0x26e1, 0x0)
    82  sendmsg$nl_xfrm(r0, &(0x7f0000000240)={0x0, 0x0, &(0x7f0000000080)={&(0x7f00000001c0)=ANY=[], 0x33fe0}}, 0x0)`,
    83  			func(p *Prog) Arg {
    84  				sendMsgCall := p.Calls[1]
    85  				msgHdr := sendMsgCall.Args[1].(*PointerArg).Res
    86  				return msgHdr
    87  			},
    88  			[]any{
    89  				uint32(3), // Num regions.
    90  
    91  				// Region definitions: (offset, size) pairs.
    92  				uint32(0), uint32(0x38),
    93  				uint32(0x40), uint32(0x10),
    94  				uint32(0x58), uint32(0x0),
    95  			},
    96  			[]any{
    97  				uint32(3),   // Num entries.
    98  				uint32(0x8), // Bytes of padding.
    99  
   100  				// Relocation definitions: (source region, offset, dest region) triplets.
   101  				uint32(0), uint32(0x00), kFuzzTestRegionIDNull,
   102  				uint32(0), uint32(0x10), uint32(1),
   103  				uint32(1), uint32(0x00), uint32(2),
   104  				uint64(0x0), // 8 bytes of padding.
   105  			},
   106  			[]any{
   107  				// Region 0 data.
   108  				kFuzzTestPlaceHolderPtr, // `addr` field, placeholder pointer.
   109  				uint32(0x0),             // `addrlen`.
   110  				uint32(0x0),             // `pad[4]`.
   111  				kFuzzTestPlaceHolderPtr, // `vec` field, placeholder pointer.
   112  				uint64(0x1),             // `vlen`.
   113  				uint64(0x0),             // `ctrl`.
   114  				uint64(0x0),             // `ctrllen`.
   115  				uint32(0x0),             // `f`.
   116  				uint32(0x0),             // `pad[4]`.
   117  
   118  				uint64(0x0), // 8 bytes of padding between regions.
   119  
   120  				// Region 1 data.
   121  				kFuzzTestPlaceHolderPtr, // `addr` field, placeholder pointer.
   122  				uint64(0x033fe0),        // `len`.
   123  
   124  				make([]byte, kFuzzTestPoisonSize), // Inter-region padding.
   125  
   126  				[]byte{}, // Region 2 data (empty).
   127  
   128  				make([]byte, kFuzzTestPoisonSize), // Tail padding.
   129  			},
   130  		},
   131  		// This test case validates the encoding of the following structure:
   132  		//      loop_info64 {
   133  		//        lo_device: const = 0x0 (8 bytes)
   134  		//        lo_inode: const = 0x0 (8 bytes)
   135  		//        lo_rdevice: const = 0x0 (8 bytes)
   136  		//        lo_offset: int64 = 0x1 (8 bytes)
   137  		//        lo_sizelimit: int64 = 0x8005 (8 bytes)
   138  		//        lo_number: const = 0x0 (4 bytes)
   139  		//        lo_enc_type: lo_encrypt_type = 0x0 (4 bytes)
   140  		//        lo_enc_key_size: int32 = 0x19 (4 bytes)
   141  		//        lo_flags: lo_flags = 0x1c (4 bytes)
   142  		//        lo_file_name: buffer: {ef 35 9f 41 3b b9 38 52 f7 d6 a4 ae 6d dd fb
   143  		//        d1 ce 5d 29 c2 ee 5e 5c a9 00 0f f8 ee 09 e7 37 ff 0e df 11 0f f4 11
   144  		//        76 39 c2 eb 4b 78 c6 60 e6 77 df 70 19 05 b9 aa fa b4 af aa f7 55 a3
   145  		//        f6 a0 04} (length 0x40) lo_crypt_name: buffer: {03 6c 47 c6 78 08 20
   146  		//        d1 cb f7 96 6d 61 fd cf 33 52 63 bd 9b ff bc c2 54 2d ed 71 03 82 59
   147  		//        ca 17 1c e1 a3 11 ef 54 ec 32 d7 1e 14 ef 3d c1 77 e9 b4 8b 00 00 00
   148  		//        00 00 00 00 00 00 00 00 00 00 00} (length 0x40) lo_enc_key: buffer:
   149  		//        {f2 83 59 73 8e 22 9a 4c 66 81 00 00 00 00 00 d3 00 e6 d6 02 00 00
   150  		//        00 00 00 00 00 00 00 00 00 01} (length 0x20) lo_init: array[int64] {
   151  		//          int64 = 0x204 (8 bytes)
   152  		//          int64 = 0x0 (8 bytes)
   153  		//        }
   154  		//      }
   155  		//    }
   156  		//  ]
   157  		{
   158  			`r0 = open(&(0x7f0000000000)='./bus\x00', 0x0, 0x0)
   159  ioctl$LOOP_SET_STATUS64(r0, 0x4c04, &(0x7f0000000540)={0x0, 0x0, 0x0, 0x1, 0x8005, 0x0, 0x0, 0x19, 0x1c, "ef359f413bb93852f7d6a4ae6dddfbd1ce5d29c2ee5e5ca9000ff8ee09e737ff0edf110ff4117639c2eb4b78c660e677df701905b9aafab4afaaf755a3f6a004", "036c47c6780820d1cbf7966d61fdcf335263bd9bffbcc2542ded71038259ca171ce1a311ef54ec32d71e14ef3dc177e9b48b00", "f28359738e229a4c66810000000000d300e6d602000000000000000000000001", [0x204]})`,
   160  			func(p *Prog) Arg {
   161  				ioctlCall := p.Calls[1]
   162  				ptrArg := ioctlCall.Args[2].(*PointerArg)
   163  				ret := ptrArg.Res
   164  				return ret
   165  			},
   166  			[]any{
   167  				uint32(1), // Num regions.
   168  
   169  				// Region definitions: (offset, size) pairs.
   170  				uint32(0), uint32(0xe8),
   171  			},
   172  			[]any{
   173  				uint32(0),        // Num entries.
   174  				uint32(12),       // Number of bytes of padding.
   175  				make([]byte, 12), // Padding.
   176  			},
   177  			[]any{
   178  				uint64(0x0),    // `lo_device`.
   179  				uint64(0x0),    // `lo_inode`.
   180  				uint64(0x0),    // `lo_rdevice`.
   181  				uint64(0x1),    // `lo_offset`.
   182  				uint64(0x8005), // `lo_sizelimit`.
   183  				uint32(0x0),    // `lo_number`.
   184  				uint32(0x0),    // `lo_enc_type`.
   185  				uint32(0x19),   // `lo_enc_key_size`.
   186  				uint32(0x1c),   // `lo_flags`.
   187  				[]byte{
   188  					0xef, 0x35, 0x9f, 0x41, 0x3b, 0xb9, 0x38, 0x52,
   189  					0xf7, 0xd6, 0xa4, 0xae, 0x6d, 0xdd, 0xfb, 0xd1,
   190  					0xce, 0x5d, 0x29, 0xc2, 0xee, 0x5e, 0x5c, 0xa9,
   191  					0x00, 0x0f, 0xf8, 0xee, 0x09, 0xe7, 0x37, 0xff,
   192  					0x0e, 0xdf, 0x11, 0x0f, 0xf4, 0x11, 0x76, 0x39,
   193  					0xc2, 0xeb, 0x4b, 0x78, 0xc6, 0x60, 0xe6, 0x77,
   194  					0xdf, 0x70, 0x19, 0x05, 0xb9, 0xaa, 0xfa, 0xb4,
   195  					0xaf, 0xaa, 0xf7, 0x55, 0xa3, 0xf6, 0xa0, 0x04,
   196  				}, // `lo_file_name`.
   197  				[]byte{
   198  					0x03, 0x6c, 0x47, 0xc6, 0x78, 0x08, 0x20, 0xd1,
   199  					0xcb, 0xf7, 0x96, 0x6d, 0x61, 0xfd, 0xcf, 0x33,
   200  					0x52, 0x63, 0xbd, 0x9b, 0xff, 0xbc, 0xc2, 0x54,
   201  					0x2d, 0xed, 0x71, 0x03, 0x82, 0x59, 0xca, 0x17,
   202  					0x1c, 0xe1, 0xa3, 0x11, 0xef, 0x54, 0xec, 0x32,
   203  					0xd7, 0x1e, 0x14, 0xef, 0x3d, 0xc1, 0x77, 0xe9,
   204  					0xb4, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   205  					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   206  				}, // `lo_crypt_name`.
   207  				[]byte{
   208  					0xf2, 0x83, 0x59, 0x73, 0x8e, 0x22, 0x9a, 0x4c,
   209  					0x66, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd3,
   210  					0x00, 0xe6, 0xd6, 0x02, 0x00, 0x00, 0x00, 0x00,
   211  					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
   212  				}, // `lo_enc_key`.
   213  				uint64(0x204), // `lo_init[0].
   214  				uint64(0x0),   // `lo_init[1].
   215  
   216  				make([]byte, kFuzzTestPoisonSize), // Tail padding.
   217  			},
   218  		},
   219  	}
   220  
   221  	for _, tc := range testCases {
   222  		testOne(t, tc)
   223  	}
   224  }
   225  
   226  func testOne(t *testing.T, tc testCase) {
   227  	target, err := GetTarget("linux", "amd64")
   228  	if err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	p, err := target.Deserialize([]byte(tc.prog), NonStrict)
   232  	if err != nil {
   233  		t.Fatal(err)
   234  	}
   235  
   236  	arg := tc.extractArg(p)
   237  	encoded := MarshallKFuzztestArg(arg)
   238  
   239  	wantPrefix := createPrefix()
   240  	wantRegionArray := createBuffer(tc.regionArray)
   241  	wantRelocTable := createBuffer(tc.relocationTable)
   242  	wantPayload := createBuffer(tc.payload)
   243  
   244  	regionArrayLen := len(wantRegionArray)
   245  	relocTableLen := len(wantRelocTable)
   246  	payloadLen := len(wantPayload)
   247  
   248  	if len(encoded) != kFuzzTestPrefixSize+regionArrayLen+relocTableLen+payloadLen {
   249  		t.Fatalf("encoded output has wrong total length: got %d, want %d",
   250  			len(encoded), regionArrayLen+relocTableLen+payloadLen)
   251  	}
   252  
   253  	gotPrefix := encoded[:kFuzzTestPrefixSize]
   254  	gotRegionArray := encoded[kFuzzTestPrefixSize : kFuzzTestPrefixSize+regionArrayLen]
   255  	gotRelocTable := encoded[kFuzzTestPrefixSize+regionArrayLen : kFuzzTestPrefixSize+regionArrayLen+relocTableLen]
   256  	gotPayload := encoded[kFuzzTestPrefixSize+regionArrayLen+relocTableLen:]
   257  
   258  	assert.Equal(t, wantPrefix, gotPrefix)
   259  	assert.Equal(t, wantRegionArray, gotRegionArray)
   260  	assert.Equal(t, wantRelocTable, gotRelocTable)
   261  	assert.Equal(t, wantPayload, gotPayload)
   262  }