/src/thrift/lib/cpp/test/fuzz/FuzzCommon.tcc
Line | Count | Source |
1 | | /* |
2 | | * Licensed to the Apache Software Foundation (ASF) under one |
3 | | * or more contributor license agreements. See the NOTICE file |
4 | | * distributed with this work for additional information |
5 | | * regarding copyright ownership. The ASF licenses this file |
6 | | * to you under the Apache License, Version 2.0 (the |
7 | | * "License"); you may not use this file except in compliance |
8 | | * with the License. You may obtain a copy of the License at |
9 | | * |
10 | | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | | * |
12 | | * Unless required by applicable law or agreed to in writing, |
13 | | * software distributed under the License is distributed on an |
14 | | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
15 | | * KIND, either express or implied. See the License for the |
16 | | * specific language governing permissions and limitations |
17 | | * under the License. |
18 | | */ |
19 | | |
20 | | #ifndef THRIFT_TEST_FUZZ_COMMON_TCC_ |
21 | | #define THRIFT_TEST_FUZZ_COMMON_TCC_ |
22 | | |
23 | | #include <stddef.h> |
24 | | #include <stdint.h> |
25 | | #include <memory> |
26 | | #include <cmath> |
27 | | #include <iostream> |
28 | | |
29 | | #include <thrift/protocol/TDebugProtocol.h> |
30 | | #include <thrift/transport/TBufferTransports.h> |
31 | | #include <thrift/TToString.h> |
32 | | #include <thrift/TConfiguration.h> |
33 | | |
34 | | #include "gen-cpp/FuzzTest_types.h" |
35 | | |
36 | | namespace apache { namespace thrift { namespace fuzzer { |
37 | | |
38 | | using namespace apache::thrift::transport; |
39 | | using namespace apache::thrift::protocol; |
40 | | using namespace fuzz; |
41 | | |
42 | | // 10MB message size limit to prevent over-allocation during fuzzing |
43 | | const int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024; |
44 | | |
45 | 2 | inline bool is_nan_false_positive(FuzzTest& test1, FuzzTest& test2) { |
46 | 2 | BasicTypes& b1 = test1.basic; |
47 | 2 | BasicTypes& b2 = test2.basic; |
48 | 2 | if (std::isnan(b1.double_field) && std::isnan(b2.double_field)) { |
49 | 2 | b1.double_field = 0.0; |
50 | 2 | b2.double_field = 0.0; |
51 | 2 | } |
52 | | |
53 | | // Check for NaN in containers if they contain doubles |
54 | | // This is a simplified version - may need adjustment based on actual schema |
55 | | |
56 | 2 | return test1 == test2; |
57 | 2 | } |
58 | | |
59 | | // Simple parse-only fuzzer |
60 | | template<typename ProtocolType> |
61 | 9.38k | int fuzz_parse(const uint8_t* data, size_t size) { |
62 | 9.38k | try { |
63 | 9.38k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); |
64 | 9.38k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); |
65 | 9.38k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); |
66 | | |
67 | 9.38k | FuzzTest test; |
68 | 9.38k | test.read(proto.get()); |
69 | 9.38k | } catch (const TException&) { |
70 | | // Ignore any Thrift exceptions - they're expected when fuzzing |
71 | 9.38k | } |
72 | 9.38k | return 0; |
73 | 9.38k | } int apache::thrift::fuzzer::fuzz_parse<apache::thrift::protocol::TJSONProtocol>(unsigned char const*, unsigned long) Line | Count | Source | 61 | 4.13k | int fuzz_parse(const uint8_t* data, size_t size) { | 62 | 4.13k | try { | 63 | 4.13k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 64 | 4.13k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 65 | 4.13k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 66 | | | 67 | 4.13k | FuzzTest test; | 68 | 4.13k | test.read(proto.get()); | 69 | 4.13k | } catch (const TException&) { | 70 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 71 | 4.13k | } | 72 | 4.13k | return 0; | 73 | 4.13k | } |
int apache::thrift::fuzzer::fuzz_parse<apache::thrift::protocol::TCompactProtocolT<apache::thrift::transport::TTransport> >(unsigned char const*, unsigned long) Line | Count | Source | 61 | 2.73k | int fuzz_parse(const uint8_t* data, size_t size) { | 62 | 2.73k | try { | 63 | 2.73k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 64 | 2.73k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 65 | 2.73k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 66 | | | 67 | 2.73k | FuzzTest test; | 68 | 2.73k | test.read(proto.get()); | 69 | 2.73k | } catch (const TException&) { | 70 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 71 | 2.72k | } | 72 | 2.73k | return 0; | 73 | 2.73k | } |
int apache::thrift::fuzzer::fuzz_parse<apache::thrift::protocol::TBinaryProtocolT<apache::thrift::transport::TTransport, apache::thrift::protocol::TNetworkBigEndian> >(unsigned char const*, unsigned long) Line | Count | Source | 61 | 2.51k | int fuzz_parse(const uint8_t* data, size_t size) { | 62 | 2.51k | try { | 63 | 2.51k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 64 | 2.51k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 65 | 2.51k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 66 | | | 67 | 2.51k | FuzzTest test; | 68 | 2.51k | test.read(proto.get()); | 69 | 2.51k | } catch (const TException&) { | 70 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 71 | 2.51k | } | 72 | 2.51k | return 0; | 73 | 2.51k | } |
|
74 | | |
75 | | // Roundtrip fuzzer that verifies serialization/deserialization |
76 | | template<typename ProtocolType> |
77 | 9.65k | int fuzz_roundtrip(const uint8_t* data, size_t size) { |
78 | 9.65k | try { |
79 | 9.65k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); |
80 | | |
81 | | // First parse |
82 | 9.65k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); |
83 | 9.65k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); |
84 | | |
85 | 9.65k | FuzzTest test1; |
86 | 9.65k | test1.read(proto.get()); |
87 | | |
88 | | // Serialize back |
89 | 9.65k | std::shared_ptr<TMemoryBuffer> outTrans(new TMemoryBuffer(config)); |
90 | 9.65k | std::shared_ptr<TProtocol> outProto(new ProtocolType(outTrans)); |
91 | 9.65k | test1.write(outProto.get()); |
92 | | |
93 | | // Get serialized data |
94 | 9.65k | std::string serialized = outTrans->getBufferAsString(); |
95 | | |
96 | | // Deserialize again |
97 | 9.65k | std::shared_ptr<TMemoryBuffer> reTrans(new TMemoryBuffer(config)); |
98 | 9.65k | reTrans->write((const uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.size())); |
99 | 9.65k | std::shared_ptr<TProtocol> reProto(new ProtocolType(reTrans)); |
100 | | |
101 | 9.65k | FuzzTest test2; |
102 | 9.65k | test2.read(reProto.get()); |
103 | | |
104 | | // Verify equality |
105 | 9.65k | if (!(test1 == test2) && !is_nan_false_positive(test1, test2)) { |
106 | 0 | const std::string str1(apache::thrift::ThriftDebugString(test1)); |
107 | 0 | const std::string str2(apache::thrift::ThriftDebugString(test2)); |
108 | |
|
109 | 0 | std::cout << "Expected:\n" << str1 << "\nGotten:\n" << str2 << std::endl; |
110 | |
|
111 | 0 | throw std::runtime_error("Roundtrip failed"); |
112 | 0 | } |
113 | 9.65k | } catch (const TException&) { |
114 | | // Ignore any Thrift exceptions - they're expected when fuzzing |
115 | 9.12k | } |
116 | 9.65k | return 0; |
117 | 9.65k | } int apache::thrift::fuzzer::fuzz_roundtrip<apache::thrift::protocol::TJSONProtocol>(unsigned char const*, unsigned long) Line | Count | Source | 77 | 2.54k | int fuzz_roundtrip(const uint8_t* data, size_t size) { | 78 | 2.54k | try { | 79 | 2.54k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 80 | | | 81 | | // First parse | 82 | 2.54k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 83 | 2.54k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 84 | | | 85 | 2.54k | FuzzTest test1; | 86 | 2.54k | test1.read(proto.get()); | 87 | | | 88 | | // Serialize back | 89 | 2.54k | std::shared_ptr<TMemoryBuffer> outTrans(new TMemoryBuffer(config)); | 90 | 2.54k | std::shared_ptr<TProtocol> outProto(new ProtocolType(outTrans)); | 91 | 2.54k | test1.write(outProto.get()); | 92 | | | 93 | | // Get serialized data | 94 | 2.54k | std::string serialized = outTrans->getBufferAsString(); | 95 | | | 96 | | // Deserialize again | 97 | 2.54k | std::shared_ptr<TMemoryBuffer> reTrans(new TMemoryBuffer(config)); | 98 | 2.54k | reTrans->write((const uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.size())); | 99 | 2.54k | std::shared_ptr<TProtocol> reProto(new ProtocolType(reTrans)); | 100 | | | 101 | 2.54k | FuzzTest test2; | 102 | 2.54k | test2.read(reProto.get()); | 103 | | | 104 | | // Verify equality | 105 | 2.54k | if (!(test1 == test2) && !is_nan_false_positive(test1, test2)) { | 106 | 0 | const std::string str1(apache::thrift::ThriftDebugString(test1)); | 107 | 0 | const std::string str2(apache::thrift::ThriftDebugString(test2)); | 108 | |
| 109 | 0 | std::cout << "Expected:\n" << str1 << "\nGotten:\n" << str2 << std::endl; | 110 | |
| 111 | 0 | throw std::runtime_error("Roundtrip failed"); | 112 | 0 | } | 113 | 2.54k | } catch (const TException&) { | 114 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 115 | 2.54k | } | 116 | 2.54k | return 0; | 117 | 2.54k | } |
int apache::thrift::fuzzer::fuzz_roundtrip<apache::thrift::protocol::TCompactProtocolT<apache::thrift::transport::TTransport> >(unsigned char const*, unsigned long) Line | Count | Source | 77 | 3.34k | int fuzz_roundtrip(const uint8_t* data, size_t size) { | 78 | 3.34k | try { | 79 | 3.34k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 80 | | | 81 | | // First parse | 82 | 3.34k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 83 | 3.34k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 84 | | | 85 | 3.34k | FuzzTest test1; | 86 | 3.34k | test1.read(proto.get()); | 87 | | | 88 | | // Serialize back | 89 | 3.34k | std::shared_ptr<TMemoryBuffer> outTrans(new TMemoryBuffer(config)); | 90 | 3.34k | std::shared_ptr<TProtocol> outProto(new ProtocolType(outTrans)); | 91 | 3.34k | test1.write(outProto.get()); | 92 | | | 93 | | // Get serialized data | 94 | 3.34k | std::string serialized = outTrans->getBufferAsString(); | 95 | | | 96 | | // Deserialize again | 97 | 3.34k | std::shared_ptr<TMemoryBuffer> reTrans(new TMemoryBuffer(config)); | 98 | 3.34k | reTrans->write((const uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.size())); | 99 | 3.34k | std::shared_ptr<TProtocol> reProto(new ProtocolType(reTrans)); | 100 | | | 101 | 3.34k | FuzzTest test2; | 102 | 3.34k | test2.read(reProto.get()); | 103 | | | 104 | | // Verify equality | 105 | 3.34k | if (!(test1 == test2) && !is_nan_false_positive(test1, test2)) { | 106 | 0 | const std::string str1(apache::thrift::ThriftDebugString(test1)); | 107 | 0 | const std::string str2(apache::thrift::ThriftDebugString(test2)); | 108 | |
| 109 | 0 | std::cout << "Expected:\n" << str1 << "\nGotten:\n" << str2 << std::endl; | 110 | |
| 111 | 0 | throw std::runtime_error("Roundtrip failed"); | 112 | 0 | } | 113 | 3.34k | } catch (const TException&) { | 114 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 115 | 3.00k | } | 116 | 3.34k | return 0; | 117 | 3.34k | } |
int apache::thrift::fuzzer::fuzz_roundtrip<apache::thrift::protocol::TBinaryProtocolT<apache::thrift::transport::TTransport, apache::thrift::protocol::TNetworkBigEndian> >(unsigned char const*, unsigned long) Line | Count | Source | 77 | 3.76k | int fuzz_roundtrip(const uint8_t* data, size_t size) { | 78 | 3.76k | try { | 79 | 3.76k | std::shared_ptr<TConfiguration> config(new TConfiguration(FUZZ_MAX_MESSAGE_SIZE)); | 80 | | | 81 | | // First parse | 82 | 3.76k | std::shared_ptr<TMemoryBuffer> trans(new TMemoryBuffer(const_cast<uint8_t*>(data), size, TMemoryBuffer::OBSERVE, config)); | 83 | 3.76k | std::shared_ptr<TProtocol> proto(new ProtocolType(trans)); | 84 | | | 85 | 3.76k | FuzzTest test1; | 86 | 3.76k | test1.read(proto.get()); | 87 | | | 88 | | // Serialize back | 89 | 3.76k | std::shared_ptr<TMemoryBuffer> outTrans(new TMemoryBuffer(config)); | 90 | 3.76k | std::shared_ptr<TProtocol> outProto(new ProtocolType(outTrans)); | 91 | 3.76k | test1.write(outProto.get()); | 92 | | | 93 | | // Get serialized data | 94 | 3.76k | std::string serialized = outTrans->getBufferAsString(); | 95 | | | 96 | | // Deserialize again | 97 | 3.76k | std::shared_ptr<TMemoryBuffer> reTrans(new TMemoryBuffer(config)); | 98 | 3.76k | reTrans->write((const uint8_t*)serialized.data(), static_cast<uint32_t>(serialized.size())); | 99 | 3.76k | std::shared_ptr<TProtocol> reProto(new ProtocolType(reTrans)); | 100 | | | 101 | 3.76k | FuzzTest test2; | 102 | 3.76k | test2.read(reProto.get()); | 103 | | | 104 | | // Verify equality | 105 | 3.76k | if (!(test1 == test2) && !is_nan_false_positive(test1, test2)) { | 106 | 0 | const std::string str1(apache::thrift::ThriftDebugString(test1)); | 107 | 0 | const std::string str2(apache::thrift::ThriftDebugString(test2)); | 108 | |
| 109 | 0 | std::cout << "Expected:\n" << str1 << "\nGotten:\n" << str2 << std::endl; | 110 | |
| 111 | 0 | throw std::runtime_error("Roundtrip failed"); | 112 | 0 | } | 113 | 3.76k | } catch (const TException&) { | 114 | | // Ignore any Thrift exceptions - they're expected when fuzzing | 115 | 3.58k | } | 116 | 3.76k | return 0; | 117 | 3.76k | } |
|
118 | | |
119 | | }}} // apache::thrift::fuzzer |
120 | | |
121 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
122 | | __attribute__((weak)) int main(int argc, char** argv) { |
123 | | return 0; |
124 | | } |
125 | | #endif |
126 | | |
127 | | #endif // THRIFT_TEST_FUZZ_COMMON_TCC_ |