Skip to content

Commit 4f11cea

Browse files
committed
THRIFT-5855: Add netstd fuzzers
1 parent 4afb7d9 commit 4f11cea

20 files changed

+1087
-4
lines changed

FUZZING.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ We currently maintain fuzzers for the following languages:
2323
- Python
2424
- Rust
2525
- Swift
26-
27-
We are working on adding fuzzers for the following languages:
28-
29-
- netstd
26+
- netstd (only supported locally, and not on oss-fuzz)
3027

3128
## Fuzzer Types
3229

lib/netstd/Makefile.am

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ check-local:
2828
$(DOTNETCORE) test Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj
2929
$(DOTNETCORE) test Tests/Thrift.Tests/Thrift.Tests.csproj
3030
$(DOTNETCORE) test Tests/Thrift.IntegrationTests/Thrift.IntegrationTests.csproj
31+
$(MAKE) build-fuzzers
32+
33+
build-fuzzers:
34+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Parse -p:Engine=AFL
35+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Parse -p:Engine=Libfuzzer
36+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Roundtrip -p:Engine=AFL
37+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Binary -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer
38+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Parse -p:Engine=AFL
39+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Parse -p:Engine=Libfuzzer
40+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Roundtrip -p:Engine=AFL
41+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Compact -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer
42+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Parse -p:Engine=AFL
43+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Parse -p:Engine=Libfuzzer
44+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Roundtrip -p:Engine=AFL
45+
$(DOTNETCORE) build Tests/Thrift.FuzzTests/Thrift.FuzzTests.csproj -p:Protocol=Json -p:FuzzerType=Roundtrip -p:Engine=Libfuzzer
3146

3247
clean-local:
3348
$(RM) -r Thrift/bin
@@ -44,6 +59,8 @@ clean-local:
4459
$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.net9/obj
4560
$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/bin
4661
$(RM) -r Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/obj
62+
$(RM) -r Tests/Thrift.FuzzTests/bin
63+
$(RM) -r Tests/Thrift.FuzzTests/obj
4764

4865
distdir:
4966
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -60,6 +77,7 @@ EXTRA_DIST = \
6077
Tests/Thrift.Compile.Tests/Thrift.Compile.net8/Thrift.Compile.net8.csproj \
6178
Tests/Thrift.Compile.Tests/Thrift.Compile.net9/Thrift.Compile.net9.csproj \
6279
Tests/Thrift.Compile.Tests/Thrift.Compile.netstd2/Thrift.Compile.netstd2.csproj \
80+
Tests/Thrift.FuzzTests \
6381
Tests/Thrift.Tests/Collections \
6482
Tests/Thrift.Tests/DataModel \
6583
Tests/Thrift.Tests/Protocols \

lib/netstd/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,14 @@ Because of the different environment requirements, migration from C# takes sligh
5353
- In case you are using Thrift server event handlers: the `SetEventHandler` method now starts with an uppercase letter
5454
- and you will also have to revise the method names of all `TServerEventHandler` descendants you have in your code
5555

56+
# Fuzzing
57+
58+
We use SharpFuzz (and its libfuzzer variant) for fuzzing. This is **not** supported on oss-fuzz, so all fuzzing must be run locally (currently only tested on a linux machine)
59+
60+
To get started:
61+
62+
* Install https://github.com/Metalnem/sharpfuzz
63+
* Install https://github.com/Metalnem/libfuzzer-dotnet
64+
* Create a directory which contains the executable `libfuzzer-dotnet` and set $SHARPFUZZ_DIR to point to it.
65+
66+
Then you can run `buildfuzzers.sh` to build the fuzzers and `runfuzzer.sh` to run a given fuzzer.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// 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+
18+
using System;
19+
using System.IO;
20+
using SharpFuzz;
21+
using Thrift.Protocol;
22+
using Thrift.Transport;
23+
using Thrift.Transport.Client;
24+
25+
namespace Thrift.Tests.Protocols.Fuzzers
26+
{
27+
/// <summary>
28+
/// Base class for protocol fuzzers that handles the common fuzzing logic.
29+
/// </summary>
30+
/// <typeparam name="FuzzProtocol">The type of protocol to use for deserialization.</typeparam>
31+
public abstract class ProtocolFuzzerBase<FuzzProtocol> where FuzzProtocol : TProtocol
32+
{
33+
/// <summary>
34+
/// Environment variable that controls whether to use in-process fuzzing for AFL.
35+
/// When set to "1", uses Fuzzer.Run instead of Fuzzer.OutOfProcess.Run.
36+
/// </summary>
37+
protected const string UseInProcessFuzzingEnvVar = "THRIFT_AFL_IN_PROCESS";
38+
39+
/// <summary>
40+
/// 10MB message size limit to prevent over-allocation during fuzzing
41+
/// </summary>
42+
protected const int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024;
43+
44+
/// <summary>
45+
/// Creates a new instance of the protocol for the given transport.
46+
/// </summary>
47+
protected abstract FuzzProtocol CreateProtocol(TTransport transport);
48+
49+
/// <summary>
50+
/// Helper method that contains the core fuzzing logic.
51+
/// </summary>
52+
private void ProcessFuzzStream(Stream stream)
53+
{
54+
try
55+
{
56+
var config = new TConfiguration();
57+
config.MaxMessageSize = FUZZ_MAX_MESSAGE_SIZE;
58+
var transport = new TStreamTransport(stream, null, config);
59+
var protocol = CreateProtocol(transport);
60+
61+
var obj = new FuzzTest();
62+
obj.ReadAsync(protocol, default).GetAwaiter().GetResult();
63+
}
64+
catch (TException) { /* Expected for malformed input */ }
65+
catch (Exception) { /* Expected for malformed input */ }
66+
}
67+
68+
/// <summary>
69+
/// The core fuzzing logic that processes a single input.
70+
/// </summary>
71+
protected void ProcessFuzzInput(ReadOnlySpan<byte> span)
72+
{
73+
using var stream = new MemoryStream(span.ToArray());
74+
ProcessFuzzStream(stream);
75+
}
76+
77+
/// <summary>
78+
/// Runs the fuzzer with LibFuzzer.
79+
/// </summary>
80+
protected void RunLibFuzzer()
81+
{
82+
Fuzzer.LibFuzzer.Run(ProcessFuzzInput);
83+
}
84+
85+
/// <summary>
86+
/// Runs the fuzzer with AFL.
87+
/// </summary>
88+
protected void RunAFL()
89+
{
90+
var useInProcess = Environment.GetEnvironmentVariable(UseInProcessFuzzingEnvVar) == "1";
91+
if (useInProcess)
92+
{
93+
Fuzzer.Run(ProcessFuzzStream);
94+
}
95+
else
96+
{
97+
Fuzzer.OutOfProcess.Run(ProcessFuzzStream);
98+
}
99+
}
100+
}
101+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// 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+
18+
using System;
19+
using System.IO;
20+
using SharpFuzz;
21+
using Thrift.Protocol;
22+
using Thrift.Transport;
23+
using Thrift.Transport.Client;
24+
25+
namespace Thrift.Tests.Protocols.Fuzzers
26+
{
27+
/// <summary>
28+
/// Base class for protocol round-trip fuzzers that handles the common fuzzing logic.
29+
/// </summary>
30+
/// <typeparam name="FuzzProtocol">The type of protocol to use for serialization/deserialization.</typeparam>
31+
public abstract class ProtocolRoundtripFuzzerBase<FuzzProtocol> where FuzzProtocol : TProtocol
32+
{
33+
/// <summary>
34+
/// Environment variable that controls whether to use in-process fuzzing for AFL.
35+
/// When set to "1", uses Fuzzer.Run instead of Fuzzer.OutOfProcess.Run.
36+
/// </summary>
37+
protected const string UseInProcessFuzzingEnvVar = "THRIFT_AFL_IN_PROCESS";
38+
39+
/// <summary>
40+
/// 10MB message size limit to prevent over-allocation during fuzzing
41+
/// </summary>
42+
protected const int FUZZ_MAX_MESSAGE_SIZE = 10 * 1024 * 1024;
43+
44+
/// <summary>
45+
/// Creates a new instance of the protocol for the given transport.
46+
/// </summary>
47+
protected abstract FuzzProtocol CreateProtocol(TTransport transport);
48+
49+
/// <summary>
50+
/// Helper method that contains the core fuzzing logic.
51+
/// </summary>
52+
private void ProcessFuzzStream(Stream stream)
53+
{
54+
try
55+
{
56+
// First deserialize the input
57+
var config = new TConfiguration();
58+
config.MaxMessageSize = FUZZ_MAX_MESSAGE_SIZE;
59+
var inputTransport = new TStreamTransport(stream, null, config);
60+
var inputProtocol = CreateProtocol(inputTransport);
61+
62+
var inputObj = new FuzzTest();
63+
inputObj.ReadAsync(inputProtocol, default).GetAwaiter().GetResult();
64+
65+
// Now serialize it back
66+
using var outputStream = new MemoryStream();
67+
var outputTransport = new TStreamTransport(null, outputStream, config);
68+
var outputProtocol = CreateProtocol(outputTransport);
69+
inputObj.WriteAsync(outputProtocol, default).GetAwaiter().GetResult();
70+
outputTransport.FlushAsync(default).GetAwaiter().GetResult();
71+
72+
// Get the serialized bytes and deserialize again
73+
var serialized = outputStream.ToArray();
74+
using var reStream = new MemoryStream(serialized);
75+
var reTransport = new TStreamTransport(reStream, null, config);
76+
var reProtocol = CreateProtocol(reTransport);
77+
78+
var outputObj = new FuzzTest();
79+
outputObj.ReadAsync(reProtocol, default).GetAwaiter().GetResult();
80+
81+
// Compare the objects
82+
if (!inputObj.Equals(outputObj))
83+
{
84+
throw new Exception("Round-trip objects are not equal");
85+
}
86+
}
87+
catch (TException) { /* Expected for malformed input */ }
88+
catch (Exception) { /* Expected for malformed input */ }
89+
}
90+
91+
/// <summary>
92+
/// The core fuzzing logic that processes a single input.
93+
/// </summary>
94+
protected void ProcessFuzzInput(ReadOnlySpan<byte> span)
95+
{
96+
using var stream = new MemoryStream(span.ToArray());
97+
ProcessFuzzStream(stream);
98+
}
99+
100+
/// <summary>
101+
/// Runs the fuzzer with LibFuzzer.
102+
/// </summary>
103+
protected void RunLibFuzzer()
104+
{
105+
Fuzzer.LibFuzzer.Run(ProcessFuzzInput);
106+
}
107+
108+
/// <summary>
109+
/// Runs the fuzzer with AFL.
110+
/// </summary>
111+
protected void RunAFL()
112+
{
113+
var useInProcess = Environment.GetEnvironmentVariable(UseInProcessFuzzingEnvVar) == "1";
114+
if (useInProcess)
115+
{
116+
Fuzzer.Run(ProcessFuzzStream);
117+
}
118+
else
119+
{
120+
Fuzzer.OutOfProcess.Run(ProcessFuzzStream);
121+
}
122+
}
123+
}
124+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// 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+
18+
using System;
19+
using System.IO;
20+
using SharpFuzz;
21+
using Thrift.Protocol;
22+
using Thrift.Transport;
23+
using Thrift.Transport.Client;
24+
25+
namespace Thrift.Tests.Protocols.Fuzzers
26+
{
27+
public class TBinaryProtocolFuzzer : ProtocolFuzzerBase<TBinaryProtocol>
28+
{
29+
protected override TBinaryProtocol CreateProtocol(TTransport transport)
30+
{
31+
return new TBinaryProtocol(transport);
32+
}
33+
34+
public static void Main(string[] args)
35+
{
36+
new TBinaryProtocolFuzzer().RunAFL();
37+
}
38+
}
39+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// 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+
18+
using System;
19+
using System.IO;
20+
using SharpFuzz;
21+
using Thrift.Protocol;
22+
using Thrift.Transport;
23+
using Thrift.Transport.Client;
24+
25+
namespace Thrift.Tests.Protocols.Fuzzers
26+
{
27+
public class TBinaryProtocolFuzzer : ProtocolFuzzerBase<TBinaryProtocol>
28+
{
29+
protected override TBinaryProtocol CreateProtocol(TTransport transport)
30+
{
31+
return new TBinaryProtocol(transport);
32+
}
33+
34+
public static void Main(string[] args)
35+
{
36+
new TBinaryProtocolFuzzer().RunLibFuzzer();
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)