react-native-macos/Libraries/WebSocket
Michelle Laurenti 86db62b7a8 fix race condition in iOS websocket implementation (#32847)
Summary:
The iOS WebSocket implementation has a race condition that causes WebSocket frame payloads to be processed incorrectly.
This can cause errors on RFC6455 compliant WebSocket servers:
- the server sends a ping frame with no payload
- the server sends a text frame with a payload longer than 125 bytes
- the client answers the ping with a pong frame echoing back the contents of the text frame

This is caused by concurrent modification of the current frame contents, that is passed by reference to the handlers. The concurrent modification happens [here](https://github.com/facebook/react-native/blob/main/Libraries/WebSocket/RCTSRWebSocket.m#L1162).

The bug was detected and fixed in the original SocketRocket repository in [this PR](https://github.com/facebookincubator/SocketRocket/pull/371). The relevant part of the fix is applied in this PR.

Resolves https://github.com/facebook/react-native/issues/30020.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[iOS] [Fixed] - Fix WebSocket control frames having payloads longer than 125 bytes

Pull Request resolved: https://github.com/facebook/react-native/pull/32847

Test Plan:
The bug is not easily and consistently reproduced, being a race condition.
We were able to reproduce it by connecting a react native app to a websocket server that sent ~100 pings per second and ~100 text frames per second. After a couple of seconds, the server receives an invalid pong message, with a payload equal to the payload of the text frame. The following is a node server that can replicate the problem on a react-native app running on iOS.

<details>

```
const { WebSocketServer } = require('ws');

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', function connection(ws) {
  const pingInterval = setInterval(() => {
    ws.ping();
  }, 10);

  const sendInterval = setInterval(() => {
    const arr = new Array(100);
    for (let i = 0; i < arr.length; i++) {
      arr[i] = Math.random();
    }
    ws.send('message with payload longer than 125 bytes: ' + arr.join(','));
  }, 10);

  ws.on('close', () => {
    clearInterval(pingInterval);
    clearInterval(sendInterval);
  });

  ws.on('error', (err) => {
    console.error(err);
    process.exit(1);
  });
});
```

</details>

Reviewed By: hramos

Differential Revision: D33486828

Pulled By: sota000

fbshipit-source-id: ba52958a584d633813e0d623d29b19999d0c617b
2022-01-20 13:56:26 -08:00
..
__mocks__ Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
__tests__ Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
NativeWebSocketModule.js Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
RCTReconnectingWebSocket.h Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
RCTReconnectingWebSocket.m Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
RCTSRWebSocket.h Backed out changeset 183744d2415b 2016-03-15 11:49:28 -07:00
RCTSRWebSocket.m fix race condition in iOS websocket implementation (#32847) 2022-01-20 13:56:26 -08:00
WebSocket.js Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
WebSocketEvent.js Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00
WebSocketInterceptor.js Update copyright headers from Facebook to Meta 2021-12-30 15:11:21 -08:00