Source/WebCore: A client MUST close a connection if it detects a masked frame
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 06:08:40 +0000 (06:08 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Mar 2012 06:08:40 +0000 (06:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81361

Patch by Li Yin <li.yin@intel.com> on 2012-03-22
Reviewed by Kent Tamura.

A server must not mask any frames that it sends to the client.
Change the test case, not mask the frames from server to client.

Test: http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html

* Modules/websockets/WebSocketChannel.cpp:
(WebCore::WebSocketChannel::processFrame):

LayoutTests: [WebSocket]A client must close a connection if it detects a masked frame
https://bugs.webkit.org/show_bug.cgi?id=81361

Patch by Li Yin <li.yin@intel.com> on 2012-03-22
Reviewed by Kent Tamura.

* http/tests/websocket/tests/hybi/invalid-masked-frames-from-server-expected.txt: Added.
* http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html: Added.
* http/tests/websocket/tests/hybi/invalid-masked-frames-from-server_wsh.py: Added.
* http/tests/websocket/tests/hybi/unmasked-frames-expected.txt: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames-expected.txt.
* http/tests/websocket/tests/hybi/unmasked-frames.html: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames.html.
* http/tests/websocket/tests/hybi/unmasked-frames_wsh.py: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames_wsh.py.
(web_socket_do_extra_handshake):
(web_socket_transfer_data):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@111829 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server_wsh.py [new file with mode: 0644]
LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames-expected.txt [moved from LayoutTests/http/tests/websocket/tests/hybi/masked-frames-expected.txt with 94% similarity]
LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames.html [moved from LayoutTests/http/tests/websocket/tests/hybi/masked-frames.html with 90% similarity]
LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames_wsh.py [moved from LayoutTests/http/tests/websocket/tests/hybi/masked-frames_wsh.py with 86% similarity]
Source/WebCore/ChangeLog
Source/WebCore/Modules/websockets/WebSocketChannel.cpp
Source/WebCore/Modules/websockets/WebSocketChannel.h

index 512cf5b472b40683ba2d34193a033b1da1bb112a..25dc115056538eca9f7a5db4d53ca12b706e577b 100644 (file)
@@ -1,3 +1,19 @@
+2012-03-22  Li Yin  <li.yin@intel.com>
+
+        [WebSocket]A client must close a connection if it detects a masked frame
+        https://bugs.webkit.org/show_bug.cgi?id=81361
+
+        Reviewed by Kent Tamura.
+
+        * http/tests/websocket/tests/hybi/invalid-masked-frames-from-server-expected.txt: Added.
+        * http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html: Added.
+        * http/tests/websocket/tests/hybi/invalid-masked-frames-from-server_wsh.py: Added.
+        * http/tests/websocket/tests/hybi/unmasked-frames-expected.txt: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames-expected.txt.
+        * http/tests/websocket/tests/hybi/unmasked-frames.html: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames.html.
+        * http/tests/websocket/tests/hybi/unmasked-frames_wsh.py: Renamed from LayoutTests/http/tests/websocket/tests/hybi/masked-frames_wsh.py.
+        (web_socket_do_extra_handshake):
+        (web_socket_transfer_data):
+
 2012-03-22  Li Yin  <li.yin@intel.com>
 
         [WebSocket]The minimal number of bytes MUST be used to encode the length
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server-expected.txt b/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server-expected.txt
new file mode 100644 (file)
index 0000000..bc09714
--- /dev/null
@@ -0,0 +1,11 @@
+CONSOLE MESSAGE: A server must not mask any frames that it sends to the client.
+Test whether WebSocket aborts the connection when it receives an masked frames from server.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+onopen() was called.
+PASS closeEvent.wasClean is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html b/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html
new file mode 100644 (file)
index 0000000..8700125
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script src="../../../../js-test-resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+description("Test whether WebSocket aborts the connection when it receives an masked frames from server.");
+
+window.jsTestIsAsync = true;
+if (window.layoutTestController)
+    layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
+
+var url = "ws://localhost:8880/websocket/tests/hybi/invalid-masked-frames-from-server";
+var ws = new WebSocket(url);
+var closeEvent;
+
+ws.onopen = function()
+{
+    debug("onopen() was called.");
+};
+
+ws.onmessage = function(event)
+{
+    var message = event.data;
+    testFailed("onmessage() was called. (message = \"" + message + "\")");
+};
+
+ws.onclose = function(event)
+{
+    closeEvent = event;
+    shouldBeFalse("closeEvent.wasClean");
+    finishJSTest();
+};
+
+</script>
+<script src="../../../../js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server_wsh.py b/LayoutTests/http/tests/websocket/tests/hybi/invalid-masked-frames-from-server_wsh.py
new file mode 100644 (file)
index 0000000..c9ef854
--- /dev/null
@@ -0,0 +1,8 @@
+from mod_pywebsocket import stream
+
+def web_socket_do_extra_handshake(request):
+    pass
+
+def web_socket_transfer_data(request):
+    # pywebsocket does not mask message by default. We need to build a frame manually to mask it.
+    request.connection.write(stream.create_text_frame('The Masked Message', mask=True))
similarity index 94%
rename from LayoutTests/http/tests/websocket/tests/hybi/masked-frames-expected.txt
rename to LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames-expected.txt
index 8f7b017ee2079aaf511b0f1bee0b4be8db7b611e..b1a568ab0ed8ee3510e712b8e66931b54a971006 100644 (file)
@@ -1,4 +1,4 @@
-Receive masked WebSocket frames.
+Receive unmasked WebSocket frames.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
similarity index 90%
rename from LayoutTests/http/tests/websocket/tests/hybi/masked-frames.html
rename to LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames.html
index 2b085edd0488f57fadd848b12ff16158c53d8712..dc22b75ce46d82bd6774652b21c3f53b4d7a8545 100644 (file)
@@ -7,13 +7,13 @@
 <div id="description"></div>
 <div id="console"></div>
 <script>
-description("Receive masked WebSocket frames.");
+description("Receive unmasked WebSocket frames.");
 
 window.jsTestIsAsync = true;
 if (window.layoutTestController)
     layoutTestController.overridePreference("WebKitHixie76WebSocketProtocolEnabled", 0);
 
-var url = "ws://localhost:8880/websocket/tests/hybi/masked-frames";
+var url = "ws://localhost:8880/websocket/tests/hybi/unmasked-frames";
 var ws = new WebSocket(url);
 var closeEvent;
 var expectedMessages = ["First message", "Fragmented message", ""];
similarity index 86%
rename from LayoutTests/http/tests/websocket/tests/hybi/masked-frames_wsh.py
rename to LayoutTests/http/tests/websocket/tests/hybi/unmasked-frames_wsh.py
index 2d03b67973348442d8b54a7bc5f98ac79f007dd6..164cc5bf22a97f9dad785be68638ddee1b2aa2c3 100644 (file)
@@ -10,12 +10,12 @@ def web_socket_do_extra_handshake(request):
 
 def web_socket_transfer_data(request):
     # pywebsocket does not mask message by default. We need to build a frame manually to mask it.
-    request.connection.write(stream.create_text_frame('First message', mask=True))
+    request.connection.write(stream.create_text_frame('First message', mask=False))
 
-    request.connection.write(stream.create_text_frame('Fragmented ', opcode=common.OPCODE_TEXT, fin=0, mask=True))
-    request.connection.write(stream.create_text_frame('message', opcode=common.OPCODE_CONTINUATION, fin=1, mask=True))
+    request.connection.write(stream.create_text_frame('Fragmented ', opcode=common.OPCODE_TEXT, fin=0, mask=False))
+    request.connection.write(stream.create_text_frame('message', opcode=common.OPCODE_CONTINUATION, fin=1, mask=False))
 
-    request.connection.write(stream.create_text_frame('', mask=True))
+    request.connection.write(stream.create_text_frame('', mask=False))
 
     msgutil.send_message(request, 'END')
 
@@ -30,6 +30,6 @@ def web_socket_transfer_data(request):
 
     # Send a masked close frame. Clients should be able to handle this frame and
     # the WebSocket object should be closed cleanly.
-    request.connection.write(stream.create_close_frame('', mask=True))
+    request.connection.write(stream.create_close_frame('', mask=False))
 
     raise handshake.AbortedByUserException('Abort the connection') # Prevents pywebsocket from starting its own closing handshake.
index ea9b32379efea40cc5290795415131d49f565870..cd145376d9591fa75d225144a9c8cc151f45b59a 100644 (file)
@@ -1,3 +1,18 @@
+2012-03-22  Li Yin  <li.yin@intel.com>
+
+        A client MUST close a connection if it detects a masked frame
+        https://bugs.webkit.org/show_bug.cgi?id=81361
+
+        Reviewed by Kent Tamura.
+
+        A server must not mask any frames that it sends to the client.
+        Change the test case, not mask the frames from server to client.
+        
+        Test: http/tests/websocket/tests/hybi/invalid-masked-frames-from-server.html
+
+        * Modules/websockets/WebSocketChannel.cpp:
+        (WebCore::WebSocketChannel::processFrame):
+
 2012-03-22  Li Yin  <li.yin@intel.com>
 
         [WebSocket]The minimal number of bytes MUST be used to encode the length
index 124e6bd80e437f774ffd4772651e439d5e48c5d2..104813b0df6ba98b5c01d9f2e3e0313c3e44f3b5 100644 (file)
@@ -569,6 +569,10 @@ WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(WebSocketFrame&
     unsigned char opCode = firstByte & opCodeMask;
 
     bool masked = secondByte & maskBit;
+    if (masked) {
+        fail("A server must not mask any frames that it sends to the client.");
+        return FrameError;
+    }
     uint64_t payloadLength64 = secondByte & payloadLengthMask;
     if (payloadLength64 > maxPayloadLengthWithoutExtendedLengthField) {
         int extendedPayloadLengthSize;
@@ -601,32 +605,24 @@ WebSocketChannel::ParseFrameResult WebSocketChannel::parseFrame(WebSocketFrame&
 #else
     static const uint64_t maxPayloadLength = 0x7FFFFFFFFFFFFFFFull;
 #endif
-    size_t maskingKeyLength = masked ? maskingKeyWidthInBytes : 0;
-    if (payloadLength64 > maxPayloadLength || payloadLength64 + maskingKeyLength > numeric_limits<size_t>::max()) {
+    if (payloadLength64 > maxPayloadLength || payloadLength64 > numeric_limits<size_t>::max()) {
         fail("WebSocket frame length too large: " + String::number(payloadLength64) + " bytes");
         return FrameError;
     }
     size_t payloadLength = static_cast<size_t>(payloadLength64);
 
-    if (static_cast<size_t>(bufferEnd - p) < maskingKeyLength + payloadLength)
+    if (static_cast<size_t>(bufferEnd - p) < payloadLength)
         return FrameIncomplete;
 
-    if (masked) {
-        const char* maskingKey = p;
-        char* payload = const_cast<char*>(p + maskingKeyWidthInBytes);
-        for (size_t i = 0; i < payloadLength; ++i)
-            payload[i] ^= maskingKey[i % maskingKeyWidthInBytes]; // Unmask the payload.
-    }
-
     frame.opCode = static_cast<WebSocketFrame::OpCode>(opCode);
     frame.final = final;
     frame.compress = compress;
     frame.reserved2 = reserved2;
     frame.reserved3 = reserved3;
     frame.masked = masked;
-    frame.payload = p + maskingKeyLength;
+    frame.payload = p;
     frame.payloadLength = payloadLength;
-    frameEnd = p + maskingKeyLength + payloadLength;
+    frameEnd = p + payloadLength;
     return FrameOK;
 }
 
index 62b273e08e0507755a8d99e0f22dab5c4f31eb58..542871e43a09cf83176f180e4047779de1353501 100644 (file)
@@ -138,7 +138,7 @@ private:
         FrameError
     };
 
-    ParseFrameResult parseFrame(WebSocketFrame&, const char*& frameEnd); // May modify part of m_buffer to unmask the frame.
+    ParseFrameResult parseFrame(WebSocketFrame&, const char*& frameEnd);
 
     bool processFrame();
     bool processFrameHixie76();