Просмотр исходного кода

In-source fuzzing support (#1177)

Motivation:

gRPC Swift was recently added to OSS Fuzz. Doing so required patching
gRPC to make the server pipeline configurator public and a small driver
program to setup and run a server on an embedded channel. We should make
supporting oss-fuzz a little nicer!

Modifications:

- Add a FuzzTesting sub package with a 'ServerFuzzer' target
- Symlink in the echo model and implementation
- Add an underscore-public API addition to GRPC to configure an
  `EmbeddedChannel` for fuzzing

Result:

Better support for oss-fuzz
George Barnett 4 лет назад
Родитель
Сommit
f358115ed0

+ 7 - 0
FuzzTesting/.gitignore

@@ -0,0 +1,7 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
+DerivedData/
+.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

+ 48 - 0
FuzzTesting/Package.swift

@@ -0,0 +1,48 @@
+// swift-tools-version:5.3
+/*
+ * Copyright 2021, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import PackageDescription
+
+let package = Package(
+  name: "grpc-swift-fuzzer",
+  dependencies: [
+    .package(name: "grpc-swift", path: ".."),
+    .package(url: "https://github.com/apple/swift-nio.git", from: "2.27.0"),
+  ],
+  targets: [
+    .target(
+      name: "ServerFuzzer",
+      dependencies: [
+        .product(name: "GRPC", package: "grpc-swift"),
+        .product(name: "NIO", package: "swift-nio"),
+        .target(name: "EchoImplementation"),
+      ]
+    ),
+    .target(
+      name: "EchoModel",
+      dependencies: [
+        .product(name: "GRPC", package: "grpc-swift"),
+      ]
+    ),
+    .target(
+      name: "EchoImplementation",
+      dependencies: [
+        .product(name: "GRPC", package: "grpc-swift"),
+        .target(name: "EchoModel"),
+      ]
+    ),
+  ]
+)

+ 26 - 0
FuzzTesting/README.md

@@ -0,0 +1,26 @@
+# gRPC Swift: Fuzz Testing
+
+This package contains binaries for running fuzz testing.
+
+## Building
+
+Building the binary requires additional arguments be passed to the Swift
+compiler:
+
+```
+swift build \
+  -Xswiftc -sanitize=fuzzer,address \
+  -Xswiftc -parse-as-library
+```
+
+Note also that on macOS the Swift toolchain shipped with Xcode _does not_
+currently include fuzzing support and one must use a toolchain
+from [swift.org](https://swift.org/download/). Building on macOS therefore
+requires the above command be run via `xcrun`:
+
+```
+xcrun --toolchain swift \
+  swift build \
+    -Xswiftc -sanitize=fuzzer,address \
+    -Xswiftc -parse-as-library
+```

+ 1 - 0
FuzzTesting/Sources/EchoImplementation

@@ -0,0 +1 @@
+../../Sources/Examples/Echo/Implementation

+ 1 - 0
FuzzTesting/Sources/EchoModel

@@ -0,0 +1 @@
+../../Sources/Examples/Echo/Model

+ 47 - 0
FuzzTesting/Sources/ServerFuzzer/main.swift

@@ -0,0 +1,47 @@
+/*
+ * Copyright 2021, gRPC Authors All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import EchoImplementation
+import GRPC
+import NIO
+
+@_cdecl("LLVMFuzzerTestOneInput")
+public func test(_ start: UnsafeRawPointer, _ count: Int) -> CInt {
+  let bytes = UnsafeRawBufferPointer(start: start, count: count)
+
+  let channel = EmbeddedChannel()
+  defer {
+    _ = try? channel.finish()
+  }
+
+  let configuration = Server.Configuration(
+    target: .unixDomainSocket("/ignored"),
+    eventLoopGroup: channel.eventLoop,
+    serviceProviders: [EchoProvider()]
+  )
+
+  var buffer = channel.allocator.buffer(capacity: count)
+  buffer.writeBytes(bytes)
+
+  do {
+    try channel._configureForServerFuzzing(configuration: configuration)
+    try channel.writeInbound(buffer)
+    channel.embeddedEventLoop.run()
+  } catch {
+    // We're okay with errors.
+  }
+
+  return 0
+}

+ 6 - 0
Sources/GRPC/_EmbeddedThroughput.swift

@@ -51,4 +51,10 @@ extension EmbeddedChannel {
     )
     return self.pipeline.addHandler(codec)
   }
+
+  public func _configureForServerFuzzing(configuration: Server.Configuration) throws {
+    let configurator = GRPCServerPipelineConfigurator(configuration: configuration)
+    // We're always on an `EmbeddedEventLoop`, this is fine.
+    try self.pipeline.syncOperations.addHandler(configurator)
+  }
 }