blob: a81d6346e0cbd4c3abb0aef3d0296a13d9aa1988 [file] [log] [blame]
Hasnain Lakhani4afb7d92025-08-25 16:09:41 -07001// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18import Foundation
19import Thrift
20import Fuzz
21
22/// Generic parser that returns a parsed object from binary data - for use as a converter
23public func parseObjectWithProtocol<P: TProtocol>(
24 start: UnsafeRawPointer,
25 count: Int,
26 protocolType: P.Type) -> Fuzz.FuzzTest? {
27 let data = Data(bytes: start, count: count)
28 let transport = TMemoryBufferTransport(readBuffer: data)
29 let proto = P(on: transport)
30
31 do {
32 return try Fuzz.FuzzTest.read(from: proto)
33 } catch {
34 return nil
35 }
36}
37
38/// Test roundtrip serialization/deserialization with the specified protocol and conversion function
39public func roundtripWithProtocol<P: TProtocol>(
40 start: UnsafeRawPointer,
41 count: Int,
42 protocolType: P.Type
43) -> Int32 {
44 // Try to convert data to a test object
45 guard let testObj = parseObjectWithProtocol(start: start, count: count, protocolType: protocolType) else {
46 return 0
47 }
48
49 // Now do a roundtrip test with the converted object
50 do {
51 // Serialize
52 let writeTransport = TMemoryBufferTransport()
53 let writeProto = P(on: writeTransport)
54
55 try testObj.write(to: writeProto)
56 try writeTransport.flush()
57
58 // Deserialize
59 let readTransport = TMemoryBufferTransport(readBuffer: writeTransport.writeBuffer)
60 let readProto = P(on: readTransport)
61
62 let deserialized = try Fuzz.FuzzTest.read(from: readProto)
63
64 // This should always be true, but we check just to be sure
65 guard deserialized == testObj else {
66 fatalError("Roundtrip test failed: objects not equal after serialization/deserialization")
67 }
68
69 } catch {
70 // Catch expected exceptions
71 }
72
73 return 0
74}
75
76/// Typedef for the fuzzer function signature required by libFuzzer
77public typealias FuzzTarget = @convention(c) (UnsafeRawPointer, Int) -> Int32
78
79// Import the libFuzzer driver function
80@_silgen_name("LLVMFuzzerRunDriver")
81public func LLVMFuzzerRunDriver(
82 _ argc: UnsafeMutablePointer<Int32>,
83 _ argv: UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<CChar>>>,
84 _ userCb: @escaping @convention(c) (UnsafeRawPointer, Int) -> Int32) -> Int32
85
86// Run the libFuzzer driver with the given test function
87// We use this to get around swift compilation issues, which create main functions that otherwise
88// conflict with the libfuzzer main.
89// See more documentation here: https://llvm.org/docs/LibFuzzer.html#using-libfuzzer-as-a-library
90public func runLibFuzzerDriver(testOneInput: @escaping FuzzTarget) -> Never {
91 // Create C-style arguments to pass to LLVMFuzzerRunDriver
92 var args = CommandLine.arguments.map { strdup($0) }
93 var argc = Int32(args.count)
94 var argv = args.map { UnsafeMutablePointer<CChar>($0!) }
95 let argvPtr = UnsafeMutablePointer<UnsafeMutablePointer<CChar>>.allocate(capacity: args.count)
96 argvPtr.initialize(from: &argv, count: args.count)
97 let argvPtrPtr = UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<CChar>>>.allocate(capacity: 1)
98 argvPtrPtr.pointee = argvPtr
99
100 // Start the fuzzer engine with our test function
101 let result = LLVMFuzzerRunDriver(&argc, argvPtrPtr, testOneInput)
102
103 // Clean up
104 argvPtrPtr.deallocate()
105 argvPtr.deallocate()
106
107 exit(Int32(result))
108}