wsd: fix reading past the end of the clipboard in postProcessCopyPayload()

Add a way to search with an offset in findInVector() and then we can
avoid strstr() which assumes a null-terminated input, which may not be
the case.

	READ of size 55 at 0x606000068a56 thread T8 (docbroker_001)
	    #0 0x55d0d6718a5a in StrstrCheck(void*, char*, char const*, char const*) /home/abuild/rpmbuild/BUILD/llvm-15.0.7.src/build/../projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:652:5
	    #1 0x55d0d6718882 in __interceptor_strstr /home/abuild/rpmbuild/BUILD/llvm-15.0.7.src/build/../projects/compiler-rt/lib/asan/../sanitizer_common/sanitizer_common_interceptors.inc:669:5
	    #2 0x55d0d71872c9 in ClientSession::postProcessCopyPayload(std::shared_ptr<Message> const&)::$_4::operator()(std::vector<char, std::allocator<char>>&) const /home/vmiklos/git/collaboraonline/online-23.05-san/wsd/ClientSession.cpp:1631:31

During unit-uno-command.

Signed-off-by: Miklos Vajna <vmiklos@collabora.com>
Change-Id: Id42669d912bab4746d9f7d99a192c4c3f1d175f7
pull/8072/head
Miklos Vajna 2024-01-22 14:55:45 +01:00 committed by Caolán McNamara
parent ee708edf0d
commit 33310613cd
4 changed files with 37 additions and 15 deletions

View File

@ -978,16 +978,16 @@ namespace Util
return http_time;
}
std::size_t findInVector(const std::vector<char>& tokens, const char *cstring)
std::size_t findInVector(const std::vector<char>& tokens, const char *cstring, std::size_t offset)
{
assert(cstring);
for (std::size_t i = 0; i < tokens.size(); ++i)
for (std::size_t i = 0; i < tokens.size() - offset; ++i)
{
std::size_t j;
for (j = 0; i + j < tokens.size() && cstring[j] != '\0' && tokens[i + j] == cstring[j]; ++j)
for (j = 0; i + j < tokens.size() - offset && cstring[j] != '\0' && tokens[i + j + offset] == cstring[j]; ++j)
;
if (cstring[j] == '\0')
return i;
return i + offset;
}
return std::string::npos;
}

View File

@ -516,7 +516,7 @@ namespace Util
return oss.str();
}
size_t findInVector(const std::vector<char>& tokens, const char *cstring);
size_t findInVector(const std::vector<char>& tokens, const char *cstring, std::size_t offset = 0);
/// Trim spaces from the left. Just spaces.
inline std::string& ltrim(std::string& s)

View File

@ -70,6 +70,7 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture
#if ENABLE_DEBUG
CPPUNIT_TEST(testUtf8);
#endif
CPPUNIT_TEST(testFindInVector);
CPPUNIT_TEST_SUITE_END();
void testCOOLProtocolFunctions();
@ -100,6 +101,7 @@ class WhiteBoxTests : public CPPUNIT_NS::TestFixture
void testBytesToHex();
void testJsonUtilEscapeJSONValue();
void testUtf8();
void testFindInVector();
};
void WhiteBoxTests::testCOOLProtocolFunctions()
@ -1384,6 +1386,28 @@ void WhiteBoxTests::testUtf8()
#endif
}
void WhiteBoxTests::testFindInVector()
{
constexpr auto testname = __func__;
std::string s("fooBarfooBaz");
std::vector<char> v(s.begin(), s.end());
// Normal case, we find the first "foo".
std::size_t ret = Util::findInVector(v, "foo");
std::size_t expected = 0;
LOK_ASSERT_EQUAL(expected, ret);
// Offset, so we find the second "foo".
ret = Util::findInVector(v, "foo", 1);
expected = 6;
LOK_ASSERT_EQUAL(expected, ret);
// Negative testing.
ret = Util::findInVector(v, "blah");
expected = std::string::npos;
LOK_ASSERT_EQUAL(expected, ret);
}
CPPUNIT_TEST_SUITE_REGISTRATION(WhiteBoxTests);
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

View File

@ -1627,27 +1627,25 @@ void ClientSession::postProcessCopyPayload(const std::shared_ptr<Message>& paylo
// New-style: <div> inside <body>, that is not sanitized by Chrome.
payload->rewriteDataBody([=](std::vector<char>& data) {
const char* pos = strstr(data.data(), "<body");
if (pos)
std::size_t pos = Util::findInVector(data, "<body");
if (pos != std::string::npos)
{
pos = strstr(pos, ">");
pos = Util::findInVector(data, ">", pos);
}
if (pos)
if (pos != std::string::npos)
{
const std::string meta = getClipboardURI();
LOG_TRC("Inject clipboard cool origin of '" << meta << "'");
std::string origin = "<div id=\"meta-origin\" data-coolorigin=\"" + meta + "\">\n";
size_t offset = pos - data.data();
data.insert(data.begin() + offset + strlen(">"), origin.begin(), origin.end());
data.insert(data.begin() + pos + strlen(">"), origin.begin(), origin.end());
const char* end = "</body>";
pos = strstr(data.data(), end);
if (pos)
pos = Util::findInVector(data, end);
if (pos != std::string::npos)
{
origin = "</div>";
offset = pos - data.data();
data.insert(data.begin() + offset, origin.begin(), origin.end());
data.insert(data.begin() + pos, origin.begin(), origin.end());
}
return true;
}