Skip to content

Commit 3fe5382

Browse files
author
jeanmon
committed
Fix accounting of debug log maximum number of memory reads
1 parent 483bead commit 3fe5382

2 files changed

Lines changed: 59 additions & 2 deletions

File tree

barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/debug_log.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,20 @@ void DebugLogger::debug_log(MemoryInterface& memory,
5656
const auto fields_size_value = unconstrained_read(fields_size_offset);
5757
const uint32_t fields_size = fields_size_value.as<uint32_t>();
5858

59-
const uint32_t memory_reads =
59+
// Promote to uint64_t to avoid overflow in the addition below.
60+
const uint64_t memory_reads =
6061
1 /* level */ + 1 /* fields_size */ + message_size /* message */ + fields_size; /* fields */
6162

62-
if (memory_reads + total_memory_reads > max_memory_reads) {
63+
if (memory_reads + static_cast<uint64_t>(total_memory_reads) > static_cast<uint64_t>(max_memory_reads)) {
6364
// Unrecoverable error
6465
throw std::runtime_error(
6566
"Max debug log memory reads exceeded: " + std::to_string(memory_reads + total_memory_reads) + " > " +
6667
std::to_string(max_memory_reads));
6768
}
6869

70+
// Accounting for the memory reads
71+
total_memory_reads += memory_reads;
72+
6973
// Read message and fields from memory
7074
std::string message_as_str;
7175
for (uint32_t i = 0; i < message_size; ++i) {

barretenberg/cpp/src/barretenberg/vm2/simulation/standalone/debug_log.test.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,59 @@ TEST(DebugLogSimulationTest, MaxMemoryReadsExceeded)
101101
EXPECT_THAT(debug_logger.dump_logs(), SizeIs(0));
102102
}
103103

104+
TEST(DebugLogSimulationTest, CumulativeMaxMemoryReadsExceeded)
105+
{
106+
StrictMock<MockMemory> memory;
107+
std::vector<std::string> log_messages;
108+
// Each DEBUGLOG below consumes 5 reads (1 level + 1 fields_size + 2 message + 1 field). A budget of 9 admits the
109+
// first call but must reject the second, since the limit is cumulative across the whole simulation.
110+
DebugLogger debug_logger(
111+
DebugLogLevel::INFO, 9, [&log_messages](const std::string& message) { log_messages.push_back(message); });
112+
113+
AztecAddress contract_address = 42;
114+
MemoryAddress level_offset = 50;
115+
MemoryAddress message_offset = 100;
116+
MemoryAddress fields_offset = 200;
117+
MemoryAddress fields_size_offset = 300;
118+
119+
std::array<MemoryValue, 2> message_data = {
120+
MemoryValue::from<FF>('H'), // 'H'
121+
MemoryValue::from<FF>('i'), // 'i'
122+
};
123+
uint16_t message_size = message_data.size();
124+
125+
std::array<MemoryValue, 1> fields_data = { MemoryValue::from<FF>(42) };
126+
uint32_t fields_size = fields_data.size();
127+
128+
MemoryValue level = MemoryValue::from<uint8_t>(static_cast<uint8_t>(DebugLogLevel::FATAL));
129+
MemoryValue fields_size_value = MemoryValue::from<uint32_t>(fields_size);
130+
131+
// Both calls read the level and fields_size; only the first call gets far enough to read the message and fields
132+
// (the second throws on the cumulative bounds check before any further reads).
133+
EXPECT_CALL(memory, get(level_offset)).Times(2).WillRepeatedly(ReturnRef(level));
134+
EXPECT_CALL(memory, get(fields_size_offset)).Times(2).WillRepeatedly(ReturnRef(fields_size_value));
135+
for (uint32_t i = 0; i < message_size; ++i) {
136+
EXPECT_CALL(memory, get(message_offset + i)).WillOnce(ReturnRef(message_data[i]));
137+
}
138+
for (uint32_t i = 0; i < fields_size; ++i) {
139+
EXPECT_CALL(memory, get(fields_offset + i)).WillOnce(ReturnRef(fields_data[i]));
140+
}
141+
142+
// First call is within budget (5 <= 9) and succeeds.
143+
debug_logger.debug_log(
144+
memory, contract_address, level_offset, message_offset, message_size, fields_offset, fields_size_offset);
145+
146+
// Second call would push the cumulative total to 10 > 9 and must throw.
147+
EXPECT_THROW(
148+
debug_logger.debug_log(
149+
memory, contract_address, level_offset, message_offset, message_size, fields_offset, fields_size_offset),
150+
std::runtime_error);
151+
152+
// Only the first log was recorded.
153+
EXPECT_THAT(log_messages, ElementsAre("DEBUGLOG(fatal): Hi: [0x2a]"));
154+
EXPECT_THAT(debug_logger.dump_logs(), SizeIs(1));
155+
}
156+
104157
TEST(DebugLogSimulationTest, InvalidLevel)
105158
{
106159
StrictMock<MockMemory> memory;

0 commit comments

Comments
 (0)