TestWebsocketFuzzing.cpp (6991B)
1 #include "mozilla/Preferences.h" 2 3 #include "FuzzingInterface.h" 4 #include "FuzzyLayer.h" 5 #include "mozilla/SpinEventLoopUntil.h" 6 #include "nsComponentManagerUtils.h" 7 #include "nsCOMPtr.h" 8 #include "nsContentUtils.h" 9 #include "nsCycleCollector.h" 10 #include "nsIPrincipal.h" 11 #include "nsIWebSocketChannel.h" 12 #include "nsIWebSocketListener.h" 13 #include "nsNetCID.h" 14 #include "nsNetUtil.h" 15 #include "nsString.h" 16 #include "nsScriptSecurityManager.h" 17 #include "nsServiceManagerUtils.h" 18 #include "NullPrincipal.h" 19 #include "nsSandboxFlags.h" 20 21 namespace mozilla { 22 namespace net { 23 24 // Used to determine if the fuzzing target should use https:// in spec. 25 static bool fuzzWSS = true; 26 27 class FuzzingWebSocketListener final : public nsIWebSocketListener { 28 public: 29 NS_DECL_ISUPPORTS 30 NS_DECL_NSIWEBSOCKETLISTENER 31 32 FuzzingWebSocketListener() = default; 33 34 void waitUntilDoneOrStarted() { 35 SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDoneOrStarted"_ns, 36 [&]() { return mChannelDone || mChannelStarted; }); 37 } 38 39 void waitUntilDone() { 40 SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDone"_ns, 41 [&]() { return mChannelDone; }); 42 } 43 44 void waitUntilDoneOrAck() { 45 SpinEventLoopUntil("FuzzingWebSocketListener::waitUntilDoneOrAck"_ns, 46 [&]() { return mChannelDone || mChannelAck; }); 47 } 48 49 bool isStarted() { return mChannelStarted; } 50 51 private: 52 ~FuzzingWebSocketListener() = default; 53 bool mChannelDone = false; 54 bool mChannelStarted = false; 55 bool mChannelAck = false; 56 }; 57 58 NS_IMPL_ISUPPORTS(FuzzingWebSocketListener, nsIWebSocketListener) 59 60 NS_IMETHODIMP 61 FuzzingWebSocketListener::OnStart(nsISupports* aContext) { 62 FUZZING_LOG(("FuzzingWebSocketListener::OnStart")); 63 mChannelStarted = true; 64 return NS_OK; 65 } 66 67 NS_IMETHODIMP 68 FuzzingWebSocketListener::OnStop(nsISupports* aContext, nsresult aStatusCode) { 69 FUZZING_LOG(("FuzzingWebSocketListener::OnStop")); 70 mChannelDone = true; 71 return NS_OK; 72 } 73 74 NS_IMETHODIMP 75 FuzzingWebSocketListener::OnAcknowledge(nsISupports* aContext, uint32_t aSize) { 76 FUZZING_LOG(("FuzzingWebSocketListener::OnAcknowledge")); 77 mChannelAck = true; 78 return NS_OK; 79 } 80 81 NS_IMETHODIMP 82 FuzzingWebSocketListener::OnServerClose(nsISupports* aContext, uint16_t aCode, 83 const nsACString& aReason) { 84 FUZZING_LOG(("FuzzingWebSocketListener::OnServerClose")); 85 return NS_OK; 86 } 87 88 NS_IMETHODIMP 89 FuzzingWebSocketListener::OnMessageAvailable(nsISupports* aContext, 90 const nsACString& aMsg) { 91 FUZZING_LOG(("FuzzingWebSocketListener::OnMessageAvailable")); 92 return NS_OK; 93 } 94 95 NS_IMETHODIMP 96 FuzzingWebSocketListener::OnBinaryMessageAvailable(nsISupports* aContext, 97 const nsACString& aMsg) { 98 FUZZING_LOG(("FuzzingWebSocketListener::OnBinaryMessageAvailable")); 99 return NS_OK; 100 } 101 102 NS_IMETHODIMP 103 FuzzingWebSocketListener::OnError() { 104 FUZZING_LOG(("FuzzingWebSocketListener::OnError")); 105 return NS_OK; 106 } 107 108 static int FuzzingInitNetworkWebsocket(int* argc, char*** argv) { 109 Preferences::SetBool("network.dns.native-is-localhost", true); 110 Preferences::SetBool("fuzzing.necko.enabled", true); 111 Preferences::SetBool("network.websocket.delay-failed-reconnects", false); 112 Preferences::SetBool("network.http.http3.enable", false); 113 Preferences::SetInt("network.http.speculative-parallel-limit", 0); 114 Preferences::SetInt("network.proxy.type", 0); // PROXYCONFIG_DIRECT 115 return 0; 116 } 117 118 static int FuzzingInitNetworkWebsocketPlain(int* argc, char*** argv) { 119 fuzzWSS = false; 120 return FuzzingInitNetworkWebsocket(argc, argv); 121 } 122 123 static int FuzzingRunNetworkWebsocket(const uint8_t* data, size_t size) { 124 // Set the data to be processed 125 addNetworkFuzzingBuffer(data, size); 126 127 nsWeakPtr channelRef; 128 129 { 130 nsresult rv; 131 132 nsSecurityFlags secFlags; 133 secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; 134 uint32_t sandboxFlags = SANDBOXED_ORIGIN; 135 136 nsCOMPtr<nsIURI> url; 137 nsAutoCString spec; 138 RefPtr<FuzzingWebSocketListener> gWebSocketListener; 139 nsCOMPtr<nsIWebSocketChannel> gWebSocketChannel; 140 141 if (fuzzWSS) { 142 spec = "https://127.0.0.1/"; 143 gWebSocketChannel = 144 do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv); 145 } else { 146 spec = "http://127.0.0.1/"; 147 gWebSocketChannel = 148 do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv); 149 } 150 151 if (rv != NS_OK) { 152 MOZ_CRASH("Failed to create WebSocketChannel"); 153 } 154 155 if (NS_NewURI(getter_AddRefs(url), spec) != NS_OK) { 156 MOZ_CRASH("Call to NS_NewURI failed."); 157 } 158 159 nsCOMPtr<nsIPrincipal> nullPrincipal = 160 NullPrincipal::CreateWithoutOriginAttributes(); 161 162 rv = gWebSocketChannel->InitLoadInfoNative( 163 nullptr, nullPrincipal, nsContentUtils::GetSystemPrincipal(), nullptr, 164 secFlags, nsIContentPolicy::TYPE_WEBSOCKET, sandboxFlags); 165 166 if (rv != NS_OK) { 167 MOZ_CRASH("Failed to call InitLoadInfo"); 168 } 169 170 gWebSocketListener = new FuzzingWebSocketListener(); 171 172 OriginAttributes attrs; 173 rv = gWebSocketChannel->AsyncOpenNative(url, spec, attrs, 0, 174 gWebSocketListener, nullptr); 175 176 if (rv == NS_OK) { 177 FUZZING_LOG(("Successful call to AsyncOpen")); 178 179 // Wait for StartRequest or StopRequest 180 gWebSocketListener->waitUntilDoneOrStarted(); 181 182 if (gWebSocketListener->isStarted()) { 183 rv = gWebSocketChannel->SendBinaryMsg("Hello world"_ns); 184 185 if (rv != NS_OK) { 186 FUZZING_LOG(("Warning: Failed to call SendBinaryMsg")); 187 } else { 188 gWebSocketListener->waitUntilDoneOrAck(); 189 } 190 191 rv = gWebSocketChannel->Close(1000, ""_ns); 192 193 if (rv != NS_OK) { 194 FUZZING_LOG(("Warning: Failed to call close")); 195 } 196 } 197 198 // Wait for StopRequest 199 gWebSocketListener->waitUntilDone(); 200 } else { 201 FUZZING_LOG(("Warning: Failed to call AsyncOpen")); 202 } 203 204 channelRef = do_GetWeakReference(gWebSocketChannel); 205 } 206 207 // Wait for the channel to be destroyed 208 SpinEventLoopUntil( 209 "FuzzingRunNetworkWebsocket(channel == nullptr)"_ns, [&]() -> bool { 210 nsCycleCollector_collect(CCReason::API, nullptr); 211 nsCOMPtr<nsIWebSocketChannel> channel = do_QueryReferent(channelRef); 212 return channel == nullptr; 213 }); 214 215 if (!signalNetworkFuzzingDone()) { 216 // Wait for the connection to indicate closed 217 SpinEventLoopUntil("FuzzingRunNetworkWebsocket(gFuzzingConnClosed)"_ns, 218 [&]() -> bool { return gFuzzingConnClosed; }); 219 } 220 221 return 0; 222 } 223 224 MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkWebsocket, 225 FuzzingRunNetworkWebsocket, NetworkWebsocket); 226 MOZ_FUZZING_INTERFACE_RAW(FuzzingInitNetworkWebsocketPlain, 227 FuzzingRunNetworkWebsocket, NetworkWebsocketPlain); 228 229 } // namespace net 230 } // namespace mozilla