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