Skip to content

Commit def4bba

Browse files
committed
MB-46900: Don't interpret BucketLogger prefix as fmt string
As part of "MB-46216: Check log format strings at compile-time (ep-engine)" (f038ea0), the behaviour of BucketLogger when it has a prefix which contained fmtlib formatting characters changed. Previously this would throw an exception which spdlog would catch, and print a log message along the lines of: "[*** LOG ERROR #1 ***] [2021-06-15 19:06:07] [globalBucketLogger] {argument not found}" However, with the changes in f038ea0 we perform the formatting of the message outselves in bucket_logger.cc, and the thrown exception isn't caught and can result in kv_engine terminating. Fix by changing how we add the BucketLogger prefixes - don't append them to the format string but instead explicilty format everything, then pass the formatted log string down to spdlog. Change-Id: Iae8212cd02e1ff9c28ae12921a10cedb7d3c6b0f Reviewed-on: http://review.couchbase.org/c/kv_engine/+/155932 Reviewed-by: Ben Huddleston <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent 6bdb30f commit def4bba

File tree

3 files changed

+42
-35
lines changed

3 files changed

+42
-35
lines changed

engines/ep/src/bucket_logger.cc

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,30 @@ void BucketLogger::logInner(spdlog::level::level_enum lvl,
5454
// Instead account any log message memory to "NonBucket" (it is only
5555
// transient and typically small - of the order of the log message length).
5656
NonBucketAllocationGuard guard;
57-
const auto prefixedFmt = prefixStringWithBucketName(engine, fmt);
58-
spdlog::logger::log(lvl, fmt::vformat(prefixedFmt, args));
57+
58+
// We want to prefix the specified message with the bucket name & optional
59+
// prefix, but we cannot be sure that bucket name / prefix
60+
// doesn't contain any fmtlib formatting characters. Therefore we build up
61+
// the log string here then pass the already-formatted string down to
62+
// spdlog directly, not using spdlog's formatting functions.
63+
fmt::memory_buffer msg;
64+
65+
// Append the id (if set)
66+
if (connectionId != 0) {
67+
fmt::format_to(msg, "{}: ", connectionId);
68+
}
69+
70+
// Append the engine name (if applicable)
71+
fmt::format_to(msg, "({}) ", engine ? engine->getName() : "No Engine");
72+
73+
// Append the given prefix (if set)
74+
if (!prefix.empty()) {
75+
fmt::format_to(msg, "{} ", prefix);
76+
}
77+
78+
// Finally format the actual user-specified format string & args.
79+
fmt::vformat_to(msg, fmt, args);
80+
spdlog::logger::log(lvl, {msg.data(), msg.size()});
5981
}
6082

6183
void BucketLogger::setLoggerAPI(ServerLogIface* api) {
@@ -93,32 +115,6 @@ void BucketLogger::unregister() {
93115
getServerLogIface()->unregister_spdlogger(name());
94116
}
95117

96-
std::string BucketLogger::prefixStringWithBucketName(
97-
const EventuallyPersistentEngine* engine, fmt::string_view fmt) {
98-
std::string fmtString;
99-
100-
// Append the id (if set)
101-
if (connectionId != 0) {
102-
fmtString.append(std::to_string(connectionId) + ": ");
103-
}
104-
105-
// Append the engine name (if applicable)
106-
if (engine) {
107-
fmtString.append('(' + std::string(engine->getName().c_str()) + ") ");
108-
} else {
109-
fmtString.append("(No Engine) ");
110-
}
111-
112-
// Append the given prefix (if set)
113-
if (!prefix.empty()) {
114-
fmtString.append(prefix + " ");
115-
}
116-
117-
// Append the original format string
118-
fmtString.append(fmt.begin(), fmt.end());
119-
return fmtString;
120-
}
121-
122118
ServerLogIface* BucketLogger::getServerLogIface() {
123119
return loggerAPI.load(std::memory_order_relaxed);
124120
}

engines/ep/src/bucket_logger.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,6 @@ class BucketLogger : public spdlog::logger {
181181
fmt::string_view fmt,
182182
fmt::format_args args);
183183

184-
/**
185-
* Helper function which prefixes the string with the name of the
186-
* specified engine, or "No Engine" if engine is null.
187-
*/
188-
std::string prefixStringWithBucketName(
189-
const EventuallyPersistentEngine* engine, fmt::string_view fmt);
190-
191184
/**
192185
* Connection ID prefix that is printed if set (printed before any other
193186
* prefix or message)

engines/ep/tests/module_tests/bucket_logger_test.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,24 @@ TEST_F(BucketLoggerTest, CriticalRawMacro) {
175175
EXPECT_EQ(1, countInFile(files.front(), "CRITICAL (No Engine) rawtext"));
176176
}
177177

178+
/**
179+
* Regression test for MB-46900 - don't error if the logger prefix contains
180+
* characters which could be interpreted as fmtlib format strings.
181+
*/
182+
TEST_F(BucketLoggerTest, FmtlibPrefix) {
183+
getGlobalBucketLogger()->prefix = "abc:{def}";
184+
EP_LOG_INFO("Test {}", "abc");
185+
}
186+
187+
/**
188+
* Regression test for MB-46900 - don't error if the logger prefix contains
189+
* characters which is an invalid fmtlib format string.
190+
*/
191+
TEST_F(BucketLoggerTest, IllegalFmtlibPrefix) {
192+
getGlobalBucketLogger()->prefix = "abc:{def";
193+
EP_LOG_INFO("Test {}", "abc");
194+
}
195+
178196
/**
179197
* Test class with ThreadGates that allows us to test the destruction of a
180198
* BucketLogger when it is owned solely by the background flushing thread.

0 commit comments

Comments
 (0)