diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/test/fuzz/FuzzingStreamListener.cpp | 44 | ||||
-rw-r--r-- | netwerk/test/fuzz/FuzzingStreamListener.h | 37 | ||||
-rw-r--r-- | netwerk/test/fuzz/TestHttpFuzzing.cpp | 297 | ||||
-rw-r--r-- | netwerk/test/fuzz/TestJARFuzzing.cpp | 187 | ||||
-rw-r--r-- | netwerk/test/fuzz/TestURIFuzzing.cpp | 238 | ||||
-rw-r--r-- | netwerk/test/fuzz/TestWebsocketFuzzing.cpp | 229 | ||||
-rw-r--r-- | netwerk/test/fuzz/moz.build | 32 | ||||
-rw-r--r-- | netwerk/test/fuzz/url_tokens.dict | 51 |
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" |