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