diff options
Diffstat (limited to 'netwerk/test/fuzz/TestWebsocketFuzzing.cpp')
-rw-r--r-- | netwerk/test/fuzz/TestWebsocketFuzzing.cpp | 229 |
1 files changed, 229 insertions, 0 deletions
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 |