What changed, and why it matters
This commit adds a hidden debug-only command-line option that lets users turn off a recently-introduced log rate limiter. By default the rate limiter stays on, so normal users are not affected. The change is mainly useful for developers running tests who need full logs. It does not by itself create a new security hole, but it does expose a switch that could theoretically be used to disable a protection against disk-filling attacks.
No immediate action required. Operators should avoid passing -nologratelimit on production nodes. Reviewers may want to confirm that the option is not exposed via RPC, GUI, or configuration file defaults in a way that could be flipped remotely.
Security signals we found
Adds a toggle for a defensive control (log rate limiting) intended to mitigate disk-filling attacks
Option is marked DEBUG_ONLY, reducing exposure to production deployments
Default remains enabled, preserving existing defensive posture
Functional tests disable the limiter, which is appropriate for test visibility but not a production concern
Evidence from the diff
The patch introduces a DEBUG_ONLY boolean argument -logratelimit (default true) and wires it into AppInitMain so that the unconditional log rate limiter is only installed when the option is true. It also makes the functional test framework pass -nologratelimit for bitcoind versions >= 299900. The rate limiter itself is unchanged; the commit only adds a toggle.
Changed components
src/init.cppsrc/init/common.cppsrc/logging.htest/functional/test_framework/test_node.pyInspect captured patch +12 / −4
diff --git a/src/init.cpp b/src/init.cpp
index 297910e2..b4119cb9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1381,10 +1381,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
}, std::chrono::minutes{5});
- LogInstance().SetRateLimiting(BCLog::LogRateLimiter::Create(
- [&scheduler](auto func, auto window) { scheduler.scheduleEvery(std::move(func), window); },
- BCLog::RATELIMIT_MAX_BYTES,
- BCLog::RATELIMIT_WINDOW));
+ if (args.GetBoolArg("-logratelimit", BCLog::DEFAULT_LOGRATELIMIT)) {
+ LogInstance().SetRateLimiting(BCLog::LogRateLimiter::Create(
+ [&scheduler](auto func, auto window) { scheduler.scheduleEvery(std::move(func), window); },
+ BCLog::RATELIMIT_MAX_BYTES,
+ BCLog::RATELIMIT_WINDOW));
+ } else {
+ LogInfo("Log rate limiting disabled");
+ }
assert(!node.validation_signals);
node.validation_signals = std::make_unique<ValidationSignals>(std::make_unique<SerialTaskRunner>(scheduler));
diff --git a/src/init/common.cpp b/src/init/common.cpp
index 25121d74..53f4215c 100644
--- a/src/init/common.cpp
+++ b/src/init/common.cpp
@@ -38,6 +38,7 @@ void AddLoggingArgs(ArgsManager& argsman)
argsman.AddArg("-logsourcelocations", strprintf("Prepend debug output with name of the originating source location (source file, line number and function name) (default: %u)", DEFAULT_LOGSOURCELOCATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-loglevelalways", strprintf("Always prepend a category and level (default: %u)", DEFAULT_LOGLEVELALWAYS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
+ argsman.AddArg("-logratelimit", strprintf("Apply rate limiting to unconditional logging to mitigate disk-filling attacks (default: %u)", BCLog::DEFAULT_LOGRATELIMIT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
}
diff --git a/src/logging.h b/src/logging.h
index 9419e245..723aeb79 100644
--- a/src/logging.h
+++ b/src/logging.h
@@ -107,6 +107,7 @@ namespace BCLog {
constexpr size_t DEFAULT_MAX_LOG_BUFFER{1'000'000}; // buffer up to 1MB of log data prior to StartLogging
constexpr uint64_t RATELIMIT_MAX_BYTES{1024 * 1024}; // maximum number of bytes per source location that can be logged within the RATELIMIT_WINDOW
constexpr auto RATELIMIT_WINDOW{1h}; // time window after which log ratelimit stats are reset
+ constexpr bool DEFAULT_LOGRATELIMIT{true};
//! Fixed window rate limiter for logging.
class LogRateLimiter
diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py
index 57b4b8c3..3b081463 100755
--- a/test/functional/test_framework/test_node.py
+++ b/test/functional/test_framework/test_node.py
@@ -136,6 +136,8 @@ class TestNode():
self.args.append("-logsourcelocations")
if self.version_is_at_least(239000):
self.args.append("-loglevel=trace")
+ if self.version_is_at_least(299900):
+ self.args.append("-nologratelimit")
# Default behavior from global -v2transport flag is added to args to persist it over restarts.
# May be overwritten in individual tests, using extra_args.
Why this scored 22/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.