Skip to content

Commit 60e19f6

Browse files
committed
[clangd] Fix use-after-free in HeaderIncluderCache
Includer cache could get into a bad state when a main file went bad and added back afterwards. This patch adds a check to invalidate to prevent that. Differential Revision: https://reviews.llvm.org/D112130
1 parent c959da9 commit 60e19f6

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

clang-tools-extra/clangd/TUScheduler.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,12 @@ class TUScheduler::HeaderIncluderCache {
286286
void remove(PathRef MainFile) {
287287
std::lock_guard<std::mutex> Lock(Mu);
288288
Association *&First = MainToFirst[MainFile];
289-
if (First)
289+
if (First) {
290290
invalidate(First);
291+
First = nullptr;
292+
}
293+
// MainToFirst entry should stay alive, as Associations might be pointing at
294+
// its key.
291295
}
292296

293297
/// Get the mainfile associated with Header, or the empty string if none.

clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,10 +1200,10 @@ TEST_F(TUSchedulerTests, IncluderCache) {
12001200
Unreliable = testPath("unreliable.h"),
12011201
OK = testPath("ok.h"),
12021202
NotIncluded = testPath("not_included.h");
1203-
class NoHeadersCDB : public GlobalCompilationDatabase {
1203+
struct NoHeadersCDB : public GlobalCompilationDatabase {
12041204
llvm::Optional<tooling::CompileCommand>
12051205
getCompileCommand(PathRef File) const override {
1206-
if (File == NoCmd || File == NotIncluded)
1206+
if (File == NoCmd || File == NotIncluded || FailAll)
12071207
return llvm::None;
12081208
auto Basic = getFallbackCommand(File);
12091209
Basic.Heuristic.clear();
@@ -1218,6 +1218,8 @@ TEST_F(TUSchedulerTests, IncluderCache) {
12181218
}
12191219
return Basic;
12201220
}
1221+
1222+
std::atomic<bool> FailAll{false};
12211223
} CDB;
12221224
TUScheduler S(CDB, optsForTest());
12231225
auto GetFlags = [&](PathRef Header) {
@@ -1288,6 +1290,21 @@ TEST_F(TUSchedulerTests, IncluderCache) {
12881290
<< "association invalidated but not reclaimed";
12891291
EXPECT_THAT(GetFlags(NotIncluded), Contains("-DMAIN2"))
12901292
<< "association still valid";
1293+
1294+
// Delete the file from CDB, it should invalidate the associations.
1295+
CDB.FailAll = true;
1296+
EXPECT_THAT(GetFlags(NoCmd), Not(Contains("-DMAIN3")))
1297+
<< "association should've been invalidated.";
1298+
// Also run update for Main3 to invalidate the preeamble to make sure next
1299+
// update populates include cache associations.
1300+
S.update(Main3, getInputs(Main3, SomeIncludes), WantDiagnostics::Yes);
1301+
EXPECT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
1302+
// Re-add the file and make sure nothing crashes.
1303+
CDB.FailAll = false;
1304+
S.update(Main3, getInputs(Main3, SomeIncludes), WantDiagnostics::Yes);
1305+
EXPECT_TRUE(S.blockUntilIdle(timeoutSeconds(10)));
1306+
EXPECT_THAT(GetFlags(NoCmd), Contains("-DMAIN3"))
1307+
<< "association invalidated and then claimed by main3";
12911308
}
12921309

12931310
TEST_F(TUSchedulerTests, PreservesLastActiveFile) {

0 commit comments

Comments
 (0)