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