summaryrefslogtreecommitdiffstats
path: root/netwerk/test/fuzz
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--netwerk/test/fuzz/FuzzingStreamListener.cpp44
-rw-r--r--netwerk/test/fuzz/FuzzingStreamListener.h37
-rw-r--r--netwerk/test/fuzz/TestHttpFuzzing.cpp297
-rw-r--r--netwerk/test/fuzz/TestJARFuzzing.cpp187
-rw-r--r--netwerk/test/fuzz/TestURIFuzzing.cpp238
-rw-r--r--netwerk/test/fuzz/TestWebsocketFuzzing.cpp229
-rw-r--r--netwerk/test/fuzz/moz.build32
-rw-r--r--netwerk/test/fuzz/url_tokens.dict51
8 files changed, 1115 insertions, 0 deletions
diff --git a/netwerk/test/fuzz/FuzzingStreamListener.cpp b/netwerk/test/fuzz/FuzzingStreamListener.cpp
new file mode 100644
index 0000000000..878b116c5b
--- /dev/null
+++ b/netwerk/test/fuzz/FuzzingStreamListener.cpp
@@ -0,0 +1,44 @@
+#include "FuzzingInterface.h"
+#include "FuzzingStreamListener.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(FuzzingStreamListener, nsIStreamListener, nsIRequestObserver)
+
+NS_IMETHODIMP
+FuzzingStreamListener::OnStartRequest(nsIRequest* aRequest) {
+ FUZZING_LOG(("FuzzingStreamListener::OnStartRequest"));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingStreamListener::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset, uint32_t aCount) {
+ FUZZING_LOG(("FuzzingStreamListener::OnDataAvailable"));
+ static uint32_t const kCopyChunkSize = 128 * 1024;
+ uint32_t toRead = std::min<uint32_t>(aCount, kCopyChunkSize);
+ nsCString data;
+
+ while (aCount) {
+ nsresult rv = NS_ReadInputStreamToString(aInputStream, data, toRead);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ aCount -= toRead;
+ toRead = std::min<uint32_t>(aCount, kCopyChunkSize);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingStreamListener::OnStopRequest(nsIRequest* aRequest,
+ nsresult aStatusCode) {
+ FUZZING_LOG(("FuzzingStreamListener::OnStopRequest"));
+ mChannelDone = true;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/fuzz/FuzzingStreamListener.h b/netwerk/test/fuzz/FuzzingStreamListener.h
new file mode 100644
index 0000000000..86f60ed102
--- /dev/null
+++ b/netwerk/test/fuzz/FuzzingStreamListener.h
@@ -0,0 +1,37 @@
+#ifndef FuzzingStreamListener_h__
+#define FuzzingStreamListener_h__
+
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsString.h"
+#include "nsNetUtil.h"
+#include "nsIStreamListener.h"
+
+namespace mozilla {
+namespace net {
+
+class FuzzingStreamListener final : public nsIStreamListener {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ FuzzingStreamListener() = default;
+
+ void waitUntilDone() {
+ SpinEventLoopUntil("net::FuzzingStreamListener::waitUntilDone"_ns,
+ [&]() { return mChannelDone; });
+ }
+
+ bool isDone() { return mChannelDone; }
+
+ private:
+ ~FuzzingStreamListener() = default;
+ bool mChannelDone = false;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif
diff --git a/netwerk/test/fuzz/TestHttpFuzzing.cpp b/netwerk/test/fuzz/TestHttpFuzzing.cpp
new file mode 100644
index 0000000000..e717b608e7
--- /dev/null
+++ b/netwerk/test/fuzz/TestHttpFuzzing.cpp
@@ -0,0 +1,297 @@
+#include "mozilla/LoadInfo.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SpinEventLoopUntil.h"
+
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsString.h"
+#include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsIChannel.h"
+#include "nsIHttpChannel.h"
+#include "nsILoadInfo.h"
+#include "nsIProxiedProtocolHandler.h"
+#include "nsIOService.h"
+#include "nsProtocolProxyService.h"
+#include "nsScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsNetUtil.h"
+#include "NullPrincipal.h"
+#include "nsCycleCollector.h"
+#include "RequestContextService.h"
+#include "nsSandboxFlags.h"
+
+#include "FuzzingInterface.h"
+#include "FuzzingStreamListener.h"
+#include "FuzzyLayer.h"
+
+namespace mozilla {
+namespace net {
+
+// Target spec and optional proxy type to use, set by the respective
+// initialization function so we can cover all combinations.
+static nsAutoCString httpSpec;
+static nsAutoCString proxyType;
+static size_t minSize;
+
+static int FuzzingInitNetworkHttp(int* argc, char*** argv) {
+ Preferences::SetBool("network.dns.native-is-localhost", true);
+ Preferences::SetBool("fuzzing.necko.enabled", true);
+ Preferences::SetInt("network.http.speculative-parallel-limit", 0);
+ Preferences::SetInt("network.http.http2.default-concurrent", 1);
+
+ if (httpSpec.IsEmpty()) {
+ httpSpec = "http://127.0.0.1/";
+ }
+
+ net_EnsurePSMInit();
+
+ return 0;
+}
+
+static int FuzzingInitNetworkHttp2(int* argc, char*** argv) {
+ httpSpec = "https://127.0.0.1/";
+ return FuzzingInitNetworkHttp(argc, argv);
+}
+
+static int FuzzingInitNetworkHttp3(int* argc, char*** argv) {
+ Preferences::SetBool("fuzzing.necko.http3", true);
+ Preferences::SetBool("network.http.http3.enable", true);
+ Preferences::SetCString("network.http.http3.alt-svc-mapping-for-testing",
+ "fuzz.bad.tld;h3=:443");
+ httpSpec = "https://fuzz.bad.tld/";
+ minSize = 1200;
+ return FuzzingInitNetworkHttp(argc, argv);
+}
+
+static int FuzzingInitNetworkHttpProxyHttp2(int* argc, char*** argv) {
+ // This is http over an https proxy
+ proxyType = "https";
+
+ return FuzzingInitNetworkHttp(argc, argv);
+}
+
+static int FuzzingInitNetworkHttp2ProxyHttp2(int* argc, char*** argv) {
+ // This is https over an https proxy
+ proxyType = "https";
+
+ return FuzzingInitNetworkHttp2(argc, argv);
+}
+
+static int FuzzingInitNetworkHttpProxyPlain(int* argc, char*** argv) {
+ // This is http over an http proxy
+ proxyType = "http";
+
+ return FuzzingInitNetworkHttp(argc, argv);
+}
+
+static int FuzzingInitNetworkHttp2ProxyPlain(int* argc, char*** argv) {
+ // This is https over an http proxy
+ proxyType = "http";
+
+ return FuzzingInitNetworkHttp2(argc, argv);
+}
+
+static int FuzzingRunNetworkHttp(const uint8_t* data, size_t size) {
+ if (size < minSize) {
+ return 0;
+ }
+
+ // Set the data to be processed
+ addNetworkFuzzingBuffer(data, size);
+
+ nsWeakPtr channelRef;
+
+ nsCOMPtr<nsIRequestContextService> rcsvc =
+ mozilla::net::RequestContextService::GetOrCreate();
+ uint64_t rcID;
+
+ {
+ nsCOMPtr<nsIURI> url;
+ nsresult rv;
+
+ if (NS_NewURI(getter_AddRefs(url), httpSpec) != NS_OK) {
+ MOZ_CRASH("Call to NS_NewURI failed.");
+ }
+
+ nsLoadFlags loadFlags;
+ loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE |
+ nsIRequest::INHIBIT_CACHING |
+ nsIRequest::LOAD_FRESH_CONNECTION |
+ nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
+ nsSecurityFlags secFlags;
+ secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
+ uint32_t sandboxFlags = SANDBOXED_ORIGIN;
+
+ nsCOMPtr<nsIChannel> channel;
+ nsCOMPtr<nsILoadInfo> loadInfo;
+
+ if (!proxyType.IsEmpty()) {
+ nsAutoCString proxyHost("127.0.0.2");
+
+ nsCOMPtr<nsIProtocolProxyService2> ps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CID);
+ if (!ps) {
+ MOZ_CRASH("Failed to create nsIProtocolProxyService2");
+ }
+
+ mozilla::net::nsProtocolProxyService* pps =
+ static_cast<mozilla::net::nsProtocolProxyService*>(ps.get());
+
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ rv = pps->NewProxyInfo(proxyType, proxyHost, 443,
+ ""_ns, // aProxyAuthorizationHeader
+ ""_ns, // aConnectionIsolationKey
+ 0, // aFlags
+ UINT32_MAX, // aFailoverTimeout
+ nullptr, // aFailoverProxy
+ getter_AddRefs(proxyInfo));
+
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Call to NewProxyInfo failed.");
+ }
+
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("do_GetIOService failed.");
+ }
+
+ nsCOMPtr<nsIProtocolHandler> handler;
+ rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler));
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("GetProtocolHandler failed.");
+ }
+
+ nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler, &rv);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("do_QueryInterface failed.");
+ }
+
+ loadInfo = new LoadInfo(
+ nsContentUtils::GetSystemPrincipal(), // loading principal
+ nsContentUtils::GetSystemPrincipal(), // triggering principal
+ nullptr, // Context
+ secFlags, nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
+ Maybe<mozilla::dom::ClientInfo>(),
+ Maybe<mozilla::dom::ServiceWorkerDescriptor>(), sandboxFlags);
+
+ rv = pph->NewProxiedChannel(url, proxyInfo,
+ 0, // aProxyResolveFlags
+ nullptr, // aProxyURI
+ loadInfo, getter_AddRefs(channel));
+
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Call to newProxiedChannel failed.");
+ }
+ } else {
+ rv = NS_NewChannel(getter_AddRefs(channel), url,
+ nsContentUtils::GetSystemPrincipal(), secFlags,
+ nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
+ nullptr, // aCookieJarSettings
+ nullptr, // aPerformanceStorage
+ nullptr, // loadGroup
+ nullptr, // aCallbacks
+ loadFlags, // aLoadFlags
+ nullptr, // aIoService
+ sandboxFlags);
+
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Call to NS_NewChannel failed.");
+ }
+
+ loadInfo = channel->LoadInfo();
+ }
+
+ if (NS_FAILED(loadInfo->SetSkipContentSniffing(true))) {
+ MOZ_CRASH("Failed to call SetSkipContentSniffing");
+ }
+
+ RefPtr<FuzzingStreamListener> gStreamListener;
+ nsCOMPtr<nsIHttpChannel> gHttpChannel;
+
+ gHttpChannel = do_QueryInterface(channel);
+ rv = gHttpChannel->SetRequestMethod("GET"_ns);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("SetRequestMethod on gHttpChannel failed.");
+ }
+
+ nsCOMPtr<nsIRequestContext> rc;
+ rv = rcsvc->NewRequestContext(getter_AddRefs(rc));
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("NewRequestContext failed.");
+ }
+ rcID = rc->GetID();
+
+ rv = gHttpChannel->SetRequestContextID(rcID);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("SetRequestContextID on gHttpChannel failed.");
+ }
+
+ if (!proxyType.IsEmpty()) {
+ // NewProxiedChannel doesn't allow us to pass loadFlags directly
+ rv = gHttpChannel->SetLoadFlags(loadFlags);
+ if (rv != NS_OK) {
+ MOZ_CRASH("SetRequestMethod on gHttpChannel failed.");
+ }
+ }
+
+ gStreamListener = new FuzzingStreamListener();
+ gHttpChannel->AsyncOpen(gStreamListener);
+
+ // Wait for StopRequest
+ gStreamListener->waitUntilDone();
+
+ bool mainPingBack = false;
+
+ NS_DispatchBackgroundTask(NS_NewRunnableFunction("Dummy", [&]() {
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction("Dummy", [&]() { mainPingBack = true; }));
+ }));
+
+ SpinEventLoopUntil("FuzzingRunNetworkHttp(mainPingBack)"_ns,
+ [&]() -> bool { return mainPingBack; });
+
+ channelRef = do_GetWeakReference(gHttpChannel);
+ }
+
+ // Wait for the channel to be destroyed
+ SpinEventLoopUntil(
+ "FuzzingRunNetworkHttp(channel == nullptr)"_ns, [&]() -> bool {
+ nsCycleCollector_collect(CCReason::API, nullptr);
+ nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(channelRef);
+ return channel == nullptr;
+ });
+
+ if (!signalNetworkFuzzingDone()) {
+ // Wait for the connection to indicate closed
+ SpinEventLoopUntil("FuzzingRunNetworkHttp(gFuzzingConnClosed)"_ns,
+ [&]() -> bool { return gFuzzingConnClosed; });
+ }
+
+ rcsvc->RemoveRequestContext(rcID);
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttp, FuzzingRunNetworkHttp,
+ NetworkHttp);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttp2, FuzzingRunNetworkHttp,
+ NetworkHttp2);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttp3, FuzzingRunNetworkHttp,
+ NetworkHttp3);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttp2ProxyHttp2,
+ FuzzingRunNetworkHttp, NetworkHttp2ProxyHttp2);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttpProxyHttp2,
+ FuzzingRunNetworkHttp, NetworkHttpProxyHttp2);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttpProxyPlain,
+ FuzzingRunNetworkHttp, NetworkHttpProxyPlain);
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkHttp2ProxyPlain,
+ FuzzingRunNetworkHttp, NetworkHttp2ProxyPlain);
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/fuzz/TestJARFuzzing.cpp b/netwerk/test/fuzz/TestJARFuzzing.cpp
new file mode 100644
index 0000000000..3d27a5bf7d
--- /dev/null
+++ b/netwerk/test/fuzz/TestJARFuzzing.cpp
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include <iostream>
+
+#include "FuzzingInterface.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIZipReader.h"
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "mozilla/Span.h"
+#include "mozilla/Unused.h"
+#include "nsIInputStream.h"
+#include "nsIStringEnumerator.h"
+
+enum FuzzMethodType {
+ eTest = 0,
+ eGetEntry,
+ eHasEntry,
+ eFindEntries,
+ eInputStream,
+ eOpenInner,
+ eLastMethod,
+};
+static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
+
+template <typename T>
+T jar_get_num(char** buf, size_t* size) {
+ if (sizeof(T) > *size) {
+ return 0;
+ }
+
+ T* iptr = reinterpret_cast<T*>(*buf);
+ *buf += sizeof(T);
+ *size -= sizeof(T);
+ return *iptr;
+}
+
+nsAutoCString jar_get_string(char** buf, size_t* size) {
+ uint8_t len = jar_get_num<uint8_t>(buf, size);
+ if (len > *size) {
+ len = static_cast<uint8_t>(*size);
+ }
+ nsAutoCString str(*buf, len);
+
+ *buf += len;
+ *size -= len;
+ return str;
+}
+
+nsresult FuzzEntries(char** buf, size_t* size, nsIZipReader* aReader,
+ const nsACString& aName) {
+ uint8_t iters = jar_get_num<uint8_t>(buf, size);
+ nsresult rv;
+ for (uint8_t i = 0; i < iters; ++i) {
+ nsAutoCString out;
+ uint64_t written;
+ nsCOMPtr<nsIZipEntry> entry;
+ nsCOMPtr<nsIInputStream> stream;
+
+ switch (jar_get_num<uint8_t>(buf, size) % eLastMethod) {
+ case eTest: {
+ rv = aReader->Test(aName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case eGetEntry: {
+ rv = aReader->GetEntry(aName, getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case eHasEntry: {
+ bool has = false;
+ rv = aReader->HasEntry(aName, &has);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ }
+ case eInputStream:
+ rv = aReader->GetInputStream(aName, getter_AddRefs(stream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ rv = NS_ReadInputStreamToString(stream, out, -1, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ default:
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult FuzzReader(char** buf, size_t* size, nsIZipReader* aReader) {
+ nsresult rv;
+ nsAutoCString name;
+ nsCOMPtr<nsIZipEntry> entry;
+ bool has = false;
+ nsAutoCString pattern;
+ nsCOMPtr<nsIUTF8StringEnumerator> enumerator;
+ nsCOMPtr<nsIInputStream> stream;
+ bool hasMore;
+ nsAutoCString out;
+ uint64_t written;
+ nsCOMPtr<nsIZipReader> newReader = do_CreateInstance(kZipReaderCID, &rv);
+ switch (jar_get_num<uint8_t>(buf, size) % eLastMethod) {
+ case eTest:
+ rv = aReader->Test(""_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case eGetEntry:
+ name = jar_get_string(buf, size);
+ rv = aReader->GetEntry(name, getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case eHasEntry:
+ name = jar_get_string(buf, size);
+ rv = aReader->HasEntry(name, &has);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case eFindEntries:
+ pattern = jar_get_string(buf, size);
+ rv = aReader->FindEntries(pattern, getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+ while (NS_SUCCEEDED(enumerator->HasMore(&hasMore)) && hasMore) {
+ if (NS_FAILED(enumerator->GetNext(name))) {
+ break;
+ }
+ rv = FuzzEntries(buf, size, aReader, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ break;
+ case eInputStream:
+ name = jar_get_string(buf, size);
+ rv = aReader->GetInputStream(name, getter_AddRefs(stream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = NS_ReadInputStreamToString(stream, out, -1, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ case eOpenInner:
+ name = jar_get_string(buf, size);
+ rv = newReader->OpenInner(aReader, name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = FuzzReader(buf, size, newReader);
+ NS_ENSURE_SUCCESS(rv, rv);
+ break;
+ default:
+ break;
+ }
+ return rv;
+}
+
+static int FuzzingRunJARParser(const uint8_t* data, size_t size) {
+ char* buf = (char*)data;
+ nsresult rv;
+
+ nsCOMPtr<nsIURI> uri;
+ nsAutoCString jardata = jar_get_string(&buf, &size);
+
+ nsCOMPtr<nsIZipReader> reader = do_CreateInstance(kZipReaderCID, &rv);
+ rv = reader->OpenMemory((void*)jardata.get(), jardata.Length());
+ NS_ENSURE_SUCCESS(rv, 0);
+
+#if 0
+ // For easily exporting the last test case that triggered a crash.
+ FILE * f = fopen("/tmp/input.jar", "wb");
+ fwrite((void*)jardata.get(), 1, jardata.Length(), f);
+ fclose(f);
+#endif
+
+ uint8_t iters = jar_get_num<uint8_t>(&buf, &size);
+ for (uint8_t i = 0; i < iters; ++i) {
+ rv = FuzzReader(&buf, &size, reader);
+ NS_ENSURE_SUCCESS(rv, 0);
+ }
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(nullptr, FuzzingRunJARParser, JARParser);
diff --git a/netwerk/test/fuzz/TestURIFuzzing.cpp b/netwerk/test/fuzz/TestURIFuzzing.cpp
new file mode 100644
index 0000000000..312a0d2db5
--- /dev/null
+++ b/netwerk/test/fuzz/TestURIFuzzing.cpp
@@ -0,0 +1,238 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include <iostream>
+
+#include "FuzzingInterface.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIURL.h"
+#include "nsIStandardURL.h"
+#include "nsIURIMutator.h"
+#include "nsNetUtil.h"
+#include "nsNetCID.h"
+#include "nsPrintfCString.h"
+#include "nsString.h"
+#include "mozilla/Encoding.h"
+#include "mozilla/Span.h"
+#include "mozilla/Unused.h"
+
+template <typename T>
+T get_numeric(char** buf, size_t* size) {
+ if (sizeof(T) > *size) {
+ return 0;
+ }
+
+ T* iptr = reinterpret_cast<T*>(*buf);
+ *buf += sizeof(T);
+ *size -= sizeof(T);
+ return *iptr;
+}
+
+nsAutoCString get_string(char** buf, size_t* size) {
+ uint8_t len = get_numeric<uint8_t>(buf, size);
+ if (len > *size) {
+ len = static_cast<uint8_t>(*size);
+ }
+ nsAutoCString str(*buf, len);
+
+ *buf += len;
+ *size -= len;
+ return str;
+}
+
+const char* charsets[] = {
+ "Big5", "EUC-JP", "EUC-KR", "gb18030",
+ "gbk", "IBM866", "ISO-2022-JP", "ISO-8859-10",
+ "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "ISO-8859-16",
+ "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5",
+ "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-8-I",
+ "KOI8-R", "KOI8-U", "macintosh", "replacement",
+ "Shift_JIS", "UTF-16BE", "UTF-16LE", "UTF-8",
+ "windows-1250", "windows-1251", "windows-1252", "windows-1253",
+ "windows-1254", "windows-1255", "windows-1256", "windows-1257",
+ "windows-1258", "windows-874", "x-mac-cyrillic", "x-user-defined"};
+
+static int FuzzingRunURIParser(const uint8_t* data, size_t size) {
+ char* buf = (char*)data;
+
+ nsCOMPtr<nsIURI> uri;
+ nsAutoCString spec = get_string(&buf, &size);
+
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
+
+ if (NS_FAILED(rv)) {
+ return 0;
+ }
+
+ uint8_t iters = get_numeric<uint8_t>(&buf, &size);
+ for (int i = 0; i < iters; i++) {
+ if (get_numeric<uint8_t>(&buf, &size) % 25 != 0) {
+ NS_MutateURI mutator(uri);
+ nsAutoCString acdata = get_string(&buf, &size);
+
+ switch (get_numeric<uint8_t>(&buf, &size) % 12) {
+ default:
+ mutator.SetSpec(acdata);
+ break;
+ case 1:
+ mutator.SetScheme(acdata);
+ break;
+ case 2:
+ mutator.SetUserPass(acdata);
+ break;
+ case 3:
+ mutator.SetUsername(acdata);
+ break;
+ case 4:
+ mutator.SetPassword(acdata);
+ break;
+ case 5:
+ mutator.SetHostPort(acdata);
+ break;
+ case 6:
+ // Called via SetHostPort
+ mutator.SetHost(acdata);
+ break;
+ case 7:
+ // Called via multiple paths
+ mutator.SetPathQueryRef(acdata);
+ break;
+ case 8:
+ mutator.SetRef(acdata);
+ break;
+ case 9:
+ mutator.SetFilePath(acdata);
+ break;
+ case 10:
+ mutator.SetQuery(acdata);
+ break;
+ case 11: {
+ const uint8_t index = get_numeric<uint8_t>(&buf, &size) %
+ (sizeof(charsets) / sizeof(char*));
+ const char* charset = charsets[index];
+ auto encoding = mozilla::Encoding::ForLabelNoReplacement(
+ mozilla::MakeStringSpan(charset));
+ mutator.SetQueryWithEncoding(acdata, encoding);
+ break;
+ }
+ }
+
+ nsresult rv = mutator.Finalize(uri);
+ if (NS_FAILED(rv)) {
+ return 0;
+ }
+ } else {
+ nsAutoCString out;
+
+ if (uri) {
+ switch (get_numeric<uint8_t>(&buf, &size) % 26) {
+ default:
+ uri->GetSpec(out);
+ break;
+ case 1:
+ uri->GetPrePath(out);
+ break;
+ case 2:
+ uri->GetScheme(out);
+ break;
+ case 3:
+ uri->GetUserPass(out);
+ break;
+ case 4:
+ uri->GetUsername(out);
+ break;
+ case 5:
+ uri->GetPassword(out);
+ break;
+ case 6:
+ uri->GetHostPort(out);
+ break;
+ case 7:
+ uri->GetHost(out);
+ break;
+ case 8: {
+ int rv;
+ uri->GetPort(&rv);
+ break;
+ }
+ case 9:
+ uri->GetPathQueryRef(out);
+ break;
+ case 10: {
+ nsCOMPtr<nsIURI> other;
+ bool rv;
+ nsAutoCString spec = get_string(&buf, &size);
+ NS_NewURI(getter_AddRefs(other), spec);
+ uri->Equals(other, &rv);
+ break;
+ }
+ case 11: {
+ nsAutoCString scheme = get_string(&buf, &size);
+ bool rv;
+ uri->SchemeIs("https", &rv);
+ break;
+ }
+ case 12: {
+ nsAutoCString in = get_string(&buf, &size);
+ uri->Resolve(in, out);
+ break;
+ }
+ case 13:
+ uri->GetAsciiSpec(out);
+ break;
+ case 14:
+ uri->GetAsciiHostPort(out);
+ break;
+ case 15:
+ uri->GetAsciiHost(out);
+ break;
+ case 16:
+ uri->GetRef(out);
+ break;
+ case 17: {
+ nsCOMPtr<nsIURI> other;
+ bool rv;
+ nsAutoCString spec = get_string(&buf, &size);
+ NS_NewURI(getter_AddRefs(other), spec);
+ uri->EqualsExceptRef(other, &rv);
+ break;
+ }
+ case 18:
+ uri->GetSpecIgnoringRef(out);
+ break;
+ case 19: {
+ bool rv;
+ uri->GetHasRef(&rv);
+ break;
+ }
+ case 20:
+ uri->GetFilePath(out);
+ break;
+ case 21:
+ uri->GetQuery(out);
+ break;
+ case 22:
+ uri->GetDisplayHost(out);
+ break;
+ case 23:
+ uri->GetDisplayHostPort(out);
+ break;
+ case 24:
+ uri->GetDisplaySpec(out);
+ break;
+ case 25:
+ uri->GetDisplayPrePath(out);
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(nullptr, FuzzingRunURIParser, URIParser);
diff --git a/netwerk/test/fuzz/TestWebsocketFuzzing.cpp b/netwerk/test/fuzz/TestWebsocketFuzzing.cpp
new file mode 100644
index 0000000000..89fd08859f
--- /dev/null
+++ b/netwerk/test/fuzz/TestWebsocketFuzzing.cpp
@@ -0,0 +1,229 @@
+#include "mozilla/Preferences.h"
+
+#include "FuzzingInterface.h"
+#include "FuzzyLayer.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollector.h"
+#include "nsIPrincipal.h"
+#include "nsIWebSocketChannel.h"
+#include "nsIWebSocketListener.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "nsScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "NullPrincipal.h"
+#include "nsSandboxFlags.h"
+
+namespace mozilla {
+namespace net {
+
+// Used to determine if the fuzzing target should use https:// in spec.
+static bool fuzzWSS = true;
+
+class FuzzingWebSocketListener final : public nsIWebSocketListener {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBSOCKETLISTENER
+
+ FuzzingWebSocketListener() = default;
+
+ void waitUntilDoneOrStarted() {
+ SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDoneOrStarted"_ns,
+ [&]() { return mChannelDone || mChannelStarted; });
+ }
+
+ void waitUntilDone() {
+ SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDone"_ns,
+ [&]() { return mChannelDone; });
+ }
+
+ void waitUntilDoneOrAck() {
+ SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDoneOrAck"_ns,
+ [&]() { return mChannelDone || mChannelAck; });
+ }
+
+ bool isStarted() { return mChannelStarted; }
+
+ private:
+ ~FuzzingWebSocketListener() = default;
+ bool mChannelDone = false;
+ bool mChannelStarted = false;
+ bool mChannelAck = false;
+};
+
+NS_IMPL_ISUPPORTS(FuzzingWebSocketListener, nsIWebSocketListener)
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnStart(nsISupports* aContext) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnStart"));
+ mChannelStarted = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnStop(nsISupports* aContext, nsresult aStatusCode) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnStop"));
+ mChannelDone = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnAcknowledge(nsISupports* aContext, uint32_t aSize) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnAcknowledge"));
+ mChannelAck = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnServerClose(nsISupports* aContext, uint16_t aCode,
+ const nsACString& aReason) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnServerClose"));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnMessageAvailable(nsISupports* aContext,
+ const nsACString& aMsg) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnMessageAvailable"));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnBinaryMessageAvailable(nsISupports* aContext,
+ const nsACString& aMsg) {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnBinaryMessageAvailable"));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FuzzingWebSocketListener::OnError() {
+ FUZZING_LOG(("FuzzingWebSocketListener::OnError"));
+ return NS_OK;
+}
+
+static int FuzzingInitNetworkWebsocket(int* argc, char*** argv) {
+ Preferences::SetBool("network.dns.native-is-localhost", true);
+ Preferences::SetBool("fuzzing.necko.enabled", true);
+ Preferences::SetBool("network.websocket.delay-failed-reconnects", false);
+ Preferences::SetInt("network.http.speculative-parallel-limit", 0);
+ Preferences::SetInt("network.proxy.type", 0); // PROXYCONFIG_DIRECT
+ return 0;
+}
+
+static int FuzzingInitNetworkWebsocketPlain(int* argc, char*** argv) {
+ fuzzWSS = false;
+ return FuzzingInitNetworkWebsocket(argc, argv);
+}
+
+static int FuzzingRunNetworkWebsocket(const uint8_t* data, size_t size) {
+ // Set the data to be processed
+ addNetworkFuzzingBuffer(data, size);
+
+ nsWeakPtr channelRef;
+
+ {
+ nsresult rv;
+
+ nsSecurityFlags secFlags;
+ secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
+ uint32_t sandboxFlags = SANDBOXED_ORIGIN;
+
+ nsCOMPtr<nsIURI> url;
+ nsAutoCString spec;
+ RefPtr<FuzzingWebSocketListener> gWebSocketListener;
+ nsCOMPtr<nsIWebSocketChannel> gWebSocketChannel;
+
+ if (fuzzWSS) {
+ spec = "https://127.0.0.1/";
+ gWebSocketChannel =
+ do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
+ } else {
+ spec = "http://127.0.0.1/";
+ gWebSocketChannel =
+ do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
+ }
+
+ if (rv != NS_OK) {
+ MOZ_CRASH("Failed to create WebSocketChannel");
+ }
+
+ if (NS_NewURI(getter_AddRefs(url), spec) != NS_OK) {
+ MOZ_CRASH("Call to NS_NewURI failed.");
+ }
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ NullPrincipal::CreateWithoutOriginAttributes();
+
+ rv = gWebSocketChannel->InitLoadInfoNative(
+ nullptr, nullPrincipal, nsContentUtils::GetSystemPrincipal(), nullptr,
+ secFlags, nsIContentPolicy::TYPE_WEBSOCKET, sandboxFlags);
+
+ if (rv != NS_OK) {
+ MOZ_CRASH("Failed to call InitLoadInfo");
+ }
+
+ gWebSocketListener = new FuzzingWebSocketListener();
+
+ OriginAttributes attrs;
+ rv = gWebSocketChannel->AsyncOpenNative(url, spec, attrs, 0,
+ gWebSocketListener, nullptr);
+
+ if (rv == NS_OK) {
+ FUZZING_LOG(("Successful call to AsyncOpen"));
+
+ // Wait for StartRequest or StopRequest
+ gWebSocketListener->waitUntilDoneOrStarted();
+
+ if (gWebSocketListener->isStarted()) {
+ rv = gWebSocketChannel->SendBinaryMsg("Hello world"_ns);
+
+ if (rv != NS_OK) {
+ FUZZING_LOG(("Warning: Failed to call SendBinaryMsg"));
+ } else {
+ gWebSocketListener->waitUntilDoneOrAck();
+ }
+
+ rv = gWebSocketChannel->Close(1000, ""_ns);
+
+ if (rv != NS_OK) {
+ FUZZING_LOG(("Warning: Failed to call close"));
+ }
+ }
+
+ // Wait for StopRequest
+ gWebSocketListener->waitUntilDone();
+ } else {
+ FUZZING_LOG(("Warning: Failed to call AsyncOpen"));
+ }
+
+ channelRef = do_GetWeakReference(gWebSocketChannel);
+ }
+
+ // Wait for the channel to be destroyed
+ SpinEventLoopUntil(
+ "FuzzingRunNetworkWebsocket(channel == nullptr)"_ns, [&]() -> bool {
+ nsCycleCollector_collect(CCReason::API, nullptr);
+ nsCOMPtr<nsIWebSocketChannel> channel = do_QueryReferent(channelRef);
+ return channel == nullptr;
+ });
+
+ if (!signalNetworkFuzzingDone()) {
+ // Wait for the connection to indicate closed
+ SpinEventLoopUntil("FuzzingRunNetworkWebsocket(gFuzzingConnClosed)"_ns,
+ [&]() -> bool { return gFuzzingConnClosed; });
+ }
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkWebsocket,
+ FuzzingRunNetworkWebsocket, NetworkWebsocket);
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkWebsocketPlain,
+ FuzzingRunNetworkWebsocket, NetworkWebsocketPlain);
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/test/fuzz/moz.build b/netwerk/test/fuzz/moz.build
new file mode 100644
index 0000000000..969b9b0b82
--- /dev/null
+++ b/netwerk/test/fuzz/moz.build
@@ -0,0 +1,32 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "FuzzingStreamListener.cpp",
+ "TestHttpFuzzing.cpp",
+ "TestJARFuzzing.cpp",
+ "TestURIFuzzing.cpp",
+ "TestWebsocketFuzzing.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/caps",
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+ "/xpcom/tests/gtest",
+]
+
+EXPORTS.mozilla.fuzzing += [
+ "FuzzingStreamListener.h",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
+
+LOCAL_INCLUDES += ["!/xpcom", "/xpcom/components"]
+
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
diff --git a/netwerk/test/fuzz/url_tokens.dict b/netwerk/test/fuzz/url_tokens.dict
new file mode 100644
index 0000000000..f297ad5de7
--- /dev/null
+++ b/netwerk/test/fuzz/url_tokens.dict
@@ -0,0 +1,51 @@
+### netwerk/base/nsStandardURL.cpp
+# Control characters
+" "
+"#"
+"/"
+":"
+"?"
+"@"
+"["
+"\\"
+"]"
+"*"
+"<"
+">"
+"|"
+"\\"
+
+# URI schemes
+"about"
+"android"
+"blob"
+"chrome"
+"data"
+"file"
+"ftp"
+"http"
+"https"
+"indexeddb"
+"jar"
+"javascript"
+"moz"
+"moz-safe-about"
+"page"
+"resource"
+"sftp"
+"smb"
+"ssh"
+"view"
+"ws"
+"wss"
+
+# URI Hosts
+"selfuri.com"
+"127.0.0.1"
+"::1"
+
+# about protocol safe paths
+"blank"
+"license"
+"logo"
+"srcdoc"