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