From bc8e9e05af56823a31005f84d9e1beff7f407dfc Mon Sep 17 00:00:00 2001 From: attila Date: Tue, 7 Oct 2025 17:07:06 +0200 Subject: [PATCH] Linux: WebBrowserComponent: Fix crash when accessing resources larger than 4k This commit reverts 8e6aeab7997c6e29f82664a6c2b272b9271274fe. The WebBrowserComponent subprocess calls tryNextRead() in an infinite loop. Prior to the reverted change this allowed it to handle the transfer of larger files, which would span multiple calls to the function. The transfer state would be remembered in the receivingLength and pos class members. The simplification in 8e6aeab7997c6e29f82664a6c2b272b9271274fe mainly comes from moving these class members into function locals, but this means, that the transfer state is lost whenever the break statements are hit. This would cause bad access during the transfer of larger files. --- .../native/juce_WebBrowserComponent_linux.cpp | 69 +++++++++---------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp b/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp index f2e8a95d1c..88849c2c9f 100644 --- a/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp +++ b/modules/juce_gui_extra/native/juce_WebBrowserComponent_linux.cpp @@ -464,24 +464,39 @@ public: { for (;;) { - char lengthBytes[sizeof (size_t)]{}; - const auto numLengthBytes = readIntoBuffer (lengthBytes); + auto len = (receivingLength ? sizeof (size_t) : bufferLength.len); - if (numLengthBytes != std::size (lengthBytes)) + if (! receivingLength) + buffer.realloc (len); + + auto* dst = (receivingLength ? bufferLength.data : buffer.getData()); + + auto actual = read (inChannel, &dst[pos], static_cast (len - pos)); + + if (actual < 0) + { + if (errno == EINTR) + continue; + + // This isn't an abort condition. The transfer of the same file can continue after + // the next call to tryNextRead. break; + } - const auto numBytesExpected = readUnaligned (lengthBytes); - buffer.reserve (numBytesExpected + 1); - buffer.resize (numBytesExpected); + pos += static_cast (actual); - if (readIntoBuffer (buffer) != numBytesExpected) - break; + if (pos == len) + { + pos = 0; - buffer.push_back (0); - parseJSON (StringRef (buffer.data())); + if (! std::exchange (receivingLength, ! receivingLength)) + { + parseJSON (String (buffer.getData(), bufferLength.len)); - if (ret == ReturnAfterMessageReceived::yes) - return; + if (ret == ReturnAfterMessageReceived::yes) + return; + } + } } if (errno != EAGAIN && errno != EWOULDBLOCK && responder != nullptr) @@ -536,37 +551,15 @@ private: } } - /* Try to fill the target buffer by reading from the input channel. - Returns the number of bytes that were successfully read. - */ - size_t readIntoBuffer (Span target) const - { - size_t pos = 0; - - while (pos != target.size()) - { - const auto bytesThisTime = read (inChannel, target.data() + pos, target.size() - pos); - - if (bytesThisTime <= 0) - { - if (bytesThisTime != 0 && errno == EINTR) - continue; - - break; - } - - pos += static_cast (bytesThisTime); - } - - return pos; - } - static Identifier getCmdIdentifier() { static Identifier Id ("cmd"); return Id; } static Identifier getParamIdentifier() { static Identifier Id ("params"); return Id; } - std::vector buffer; Responder* responder = nullptr; int inChannel = 0; + size_t pos = 0; + bool receivingLength = true; + union { char data [sizeof (size_t)]; size_t len; } bufferLength; + HeapBlock buffer; }; #define juce_g_signal_connect(instance, detailed_signal, c_handler, data) \