Skip to content

Commit eadab98

Browse files
committed
fix(diag): fix clear() not clearing, leading to use-after-free
Configuration_Loader keeps a list of diagnostics per configuration. When the configuration changes, this list is supposed to be cleared. However, Diag_List::clear() does nothing, thus old diagnostics remain. When these old diagnostics are reported, their Source_Code_Span-s reference freed memory, leading to problems. Make Diag_List::clear() clear the list as intended, fixing the bug.
1 parent a3c6764 commit eadab98

File tree

3 files changed

+30
-0
lines changed

3 files changed

+30
-0
lines changed

src/quick-lint-js/diag/diag-list.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ bool Diag_List::have_diagnostic(Diag_Type type) const {
8888

8989
void Diag_List::clear() {
9090
// Leak. this->memory should be a Linked_Bump_Allocator managed by the caller.
91+
this->first_ = nullptr;
92+
this->last_ = nullptr;
9193
}
9294
}
9395

src/quick-lint-js/diag/diag-list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class Diag_List {
6565
std::initializer_list<Diag_Type> ignored_types, const Rewind_State &);
6666
bool have_diagnostic(Diag_Type type) const;
6767

68+
// Removes all diagnostics, but does not deallocate memory.
6869
void clear();
6970

7071
private:

test/test-diag-list.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,33 @@ TEST(Test_Diag_List, not_destructing_does_not_leak) {
149149

150150
// Destruct memory, but don't destruct *diags.
151151
}
152+
153+
TEST(Test_Diag_List, clear_removes_old_diagnostics) {
154+
int count;
155+
156+
Linked_Bump_Allocator memory("test");
157+
Diag_List diags(&memory);
158+
Padded_String code(u8"hello"_sv);
159+
diags.add(Diag_Let_With_No_Bindings{.where = span_of(code)});
160+
161+
diags.clear();
162+
163+
count = 0;
164+
diags.for_each([&](Diag_Type, void*) -> void { count += 1; });
165+
EXPECT_EQ(count, 0);
166+
167+
diags.add(Diag_Expected_Parenthesis_Around_If_Condition{
168+
.where = span_of(code),
169+
.token = u8')',
170+
});
171+
172+
count = 0;
173+
diags.for_each([&](Diag_Type type, void*) -> void {
174+
EXPECT_EQ(type, Diag_Type::Diag_Expected_Parenthesis_Around_If_Condition);
175+
count += 1;
176+
});
177+
EXPECT_EQ(count, 1);
178+
}
152179
}
153180
}
154181

0 commit comments

Comments
 (0)