From b173c8854a99c3e3d25059d75ab5ebe38b603e58 Mon Sep 17 00:00:00 2001 From: Alex Tanskanen Date: Thu, 20 Feb 2020 16:12:35 +0100 Subject: [PATCH] Fix crash with too large clipboard data If too much text is copied in the session, String.fromCharCode.apply() would crash in Safari on macOS and Chrome on Linux. This commit fixes this issue by avoiding apply() altogether. Also added test to cover this issue. --- kasmweb/core/rfb.js | 6 +++++- kasmweb/tests/test.rfb.js | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/kasmweb/core/rfb.js b/kasmweb/core/rfb.js index ff2af7a..df5aa16 100644 --- a/kasmweb/core/rfb.js +++ b/kasmweb/core/rfb.js @@ -1674,7 +1674,11 @@ export default class RFB extends EventTargetMixin { streamInflator.setInput(null); if (textData !== null) { - textData = String.fromCharCode.apply(null, textData); + let tmpText = ""; + for (let i = 0; i < textData.length; i++) { + tmpText += String.fromCharCode(textData[i]); + } + textData = tmpText; textData = decodeUTF8(textData); if ((textData.length > 0) && "\0" === textData.charAt(textData.length - 1)) { diff --git a/kasmweb/tests/test.rfb.js b/kasmweb/tests/test.rfb.js index 3604098..6d49089 100644 --- a/kasmweb/tests/test.rfb.js +++ b/kasmweb/tests/test.rfb.js @@ -2514,6 +2514,38 @@ describe('Remote Frame Buffer Protocol Client', function () { client.removeEventListener("clipboard", spy); }); + it('should be able to handle large Provide messages', function () { + // repeat() is not supported in IE so a loop is needed instead + let expectedData = "hello"; + for (let i = 1; i <= 100000; i++) { + expectedData += "hello"; + } + + let data = [3, 0, 0, 0]; + const flags = [0x10, 0x00, 0x00, 0x01]; + + let text = encodeUTF8(expectedData + "\0"); + + let deflatedText = deflateWithSize(text); + + // How much data we are sending. + push32(data, toUnsigned32bit(-(4 + deflatedText.length))); + + data = data.concat(flags); + + let sendData = new Uint8Array(data.length + deflatedText.length); + sendData.set(data); + sendData.set(deflatedText, data.length); + + const spy = sinon.spy(); + client.addEventListener("clipboard", spy); + + client._sock._websocket._receive_data(sendData); + expect(spy).to.have.been.calledOnce; + expect(spy.args[0][0].detail.text).to.equal(expectedData); + client.removeEventListener("clipboard", spy); + }); + }); describe('Handle Notify', function () {