#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 rcsvc = mozilla::net::RequestContextService::GetOrCreate(); uint64_t rcID; { nsCOMPtr 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 channel; nsCOMPtr loadInfo; if (!proxyType.IsEmpty()) { nsAutoCString proxyHost("127.0.0.2"); nsCOMPtr ps = do_GetService(NS_PROTOCOLPROXYSERVICE_CID); if (!ps) { MOZ_CRASH("Failed to create nsIProtocolProxyService2"); } mozilla::net::nsProtocolProxyService* pps = static_cast(ps.get()); nsCOMPtr 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 ioService = do_GetIOService(&rv); if (NS_FAILED(rv)) { MOZ_CRASH("do_GetIOService failed."); } nsCOMPtr handler; rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler)); if (NS_FAILED(rv)) { MOZ_CRASH("GetProtocolHandler failed."); } nsCOMPtr 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(), Maybe(), 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 gStreamListener; nsCOMPtr gHttpChannel; gHttpChannel = do_QueryInterface(channel); rv = gHttpChannel->SetRequestMethod("GET"_ns); if (NS_FAILED(rv)) { MOZ_CRASH("SetRequestMethod on gHttpChannel failed."); } nsCOMPtr 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 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