Skip to content

Commit 128423f

Browse files
committedMar 17, 2017
LTO: Fix a potential race condition in the caching API.
After the call to sys::fs::exists succeeds, indicating a cache hit, we call AddFile and the client will open the file using the supplied path. If the client is using cache pruning, there is a potential race between the pruner and the client. To avoid this, change the caching API so that it provides a MemoryBuffer to the client, and have clients use that MemoryBuffer where possible. This scheme won't work with the gold plugin because the plugin API expects a file path. So we have the gold plugin use the buffer identifier as a path and live with the race for now. (Note that the gold plugin isn't actually affected by the problem at the moment because it doesn't support cache pruning.) This effectively reverts r279883 modulo the change to use the existing path in the gold plugin. Differential Revision: https://reviews.llvm.org/D31063 llvm-svn: 298020
1 parent 3735006 commit 128423f

File tree

5 files changed

+46
-26
lines changed

5 files changed

+46
-26
lines changed
 

‎lld/ELF/LTO.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,11 @@ std::vector<InputFile *> BitcodeCompiler::compile() {
150150
// specified, configure LTO to use it as the cache directory.
151151
lto::NativeObjectCache Cache;
152152
if (!Config->ThinLTOCacheDir.empty())
153-
Cache = check(lto::localCache(
154-
Config->ThinLTOCacheDir, [&](size_t Task, StringRef Path) {
155-
Files[Task] = check(MemoryBuffer::getFile(Path));
156-
}));
153+
Cache = check(
154+
lto::localCache(Config->ThinLTOCacheDir,
155+
[&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
156+
Files[Task] = std::move(MB);
157+
}));
157158

158159
checkError(LTOObj->run(
159160
[&](size_t Task) {

‎llvm/include/llvm/LTO/Caching.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,19 @@ namespace lto {
2424
/// This type defines the callback to add a pre-existing native object file
2525
/// (e.g. in a cache).
2626
///
27-
/// File callbacks must be thread safe.
28-
typedef std::function<void(unsigned Task, StringRef Path)> AddFileFn;
27+
/// MB->getBufferIdentifier() is a valid path for the file at the time that it
28+
/// was opened, but clients should prefer to access MB directly in order to
29+
/// avoid a potential race condition.
30+
///
31+
/// Buffer callbacks must be thread safe.
32+
typedef std::function<void(unsigned Task, std::unique_ptr<MemoryBuffer> MB)>
33+
AddBufferFn;
2934

3035
/// Create a local file system cache which uses the given cache directory and
3136
/// file callback. This function also creates the cache directory if it does not
3237
/// already exist.
3338
Expected<NativeObjectCache> localCache(StringRef CacheDirectoryPath,
34-
AddFileFn AddFile);
39+
AddBufferFn AddBuffer);
3540

3641
} // namespace lto
3742
} // namespace llvm

‎llvm/lib/LTO/Caching.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,42 +22,57 @@ using namespace llvm;
2222
using namespace llvm::lto;
2323

2424
Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath,
25-
AddFileFn AddFile) {
25+
AddBufferFn AddBuffer) {
2626
if (std::error_code EC = sys::fs::create_directories(CacheDirectoryPath))
2727
return errorCodeToError(EC);
2828

2929
return [=](unsigned Task, StringRef Key) -> AddStreamFn {
3030
// First, see if we have a cache hit.
3131
SmallString<64> EntryPath;
3232
sys::path::append(EntryPath, CacheDirectoryPath, Key);
33-
if (sys::fs::exists(EntryPath)) {
34-
AddFile(Task, EntryPath);
33+
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
34+
MemoryBuffer::getFile(EntryPath);
35+
if (MBOrErr) {
36+
AddBuffer(Task, std::move(*MBOrErr));
3537
return AddStreamFn();
3638
}
3739

40+
if (MBOrErr.getError() != std::errc::no_such_file_or_directory)
41+
report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
42+
": " + MBOrErr.getError().message() + "\n");
43+
3844
// This native object stream is responsible for commiting the resulting
39-
// file to the cache and calling AddFile to add it to the link.
45+
// file to the cache and calling AddBuffer to add it to the link.
4046
struct CacheStream : NativeObjectStream {
41-
AddFileFn AddFile;
47+
AddBufferFn AddBuffer;
4248
std::string TempFilename;
4349
std::string EntryPath;
4450
unsigned Task;
4551

46-
CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddFileFn AddFile,
52+
CacheStream(std::unique_ptr<raw_pwrite_stream> OS, AddBufferFn AddBuffer,
4753
std::string TempFilename, std::string EntryPath,
4854
unsigned Task)
49-
: NativeObjectStream(std::move(OS)), AddFile(std::move(AddFile)),
55+
: NativeObjectStream(std::move(OS)), AddBuffer(std::move(AddBuffer)),
5056
TempFilename(std::move(TempFilename)),
5157
EntryPath(std::move(EntryPath)), Task(Task) {}
5258

5359
~CacheStream() {
60+
// FIXME: This code could race with the cache pruner, but it is unlikely
61+
// that the cache pruner will choose to remove a newly created file.
62+
5463
// Make sure the file is closed before committing it.
5564
OS.reset();
5665
// This is atomic on POSIX systems.
5766
if (auto EC = sys::fs::rename(TempFilename, EntryPath))
5867
report_fatal_error(Twine("Failed to rename temporary file ") +
5968
TempFilename + ": " + EC.message() + "\n");
60-
AddFile(Task, EntryPath);
69+
70+
ErrorOr<std::unique_ptr<MemoryBuffer>> MBOrErr =
71+
MemoryBuffer::getFile(EntryPath);
72+
if (!MBOrErr)
73+
report_fatal_error(Twine("Failed to open cache file ") + EntryPath +
74+
": " + MBOrErr.getError().message() + "\n");
75+
AddBuffer(Task, std::move(*MBOrErr));
6176
}
6277
};
6378

@@ -77,7 +92,7 @@ Expected<NativeObjectCache> lto::localCache(StringRef CacheDirectoryPath,
7792
// This CacheStream will move the temporary file into the cache when done.
7893
return llvm::make_unique<CacheStream>(
7994
llvm::make_unique<raw_fd_ostream>(TempFD, /* ShouldClose */ true),
80-
AddFile, TempFilename.str(), EntryPath.str(), Task);
95+
AddBuffer, TempFilename.str(), EntryPath.str(), Task);
8196
};
8297
};
8398
}

‎llvm/tools/gold/gold-plugin.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -831,11 +831,15 @@ static ld_plugin_status allSymbolsReadHook() {
831831
llvm::make_unique<llvm::raw_fd_ostream>(FD, true));
832832
};
833833

834-
auto AddFile = [&](size_t Task, StringRef Path) { Filenames[Task] = Path; };
834+
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
835+
// Note that this requires that the memory buffers provided to AddBuffer are
836+
// backed by a file.
837+
Filenames[Task] = MB->getBufferIdentifier();
838+
};
835839

836840
NativeObjectCache Cache;
837841
if (!options::cache_dir.empty())
838-
Cache = check(localCache(options::cache_dir, AddFile));
842+
Cache = check(localCache(options::cache_dir, AddBuffer));
839843

840844
check(Lto->run(AddStream, Cache));
841845

‎llvm/tools/llvm-lto2/llvm-lto2.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,18 +275,13 @@ int main(int argc, char **argv) {
275275
return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
276276
};
277277

278-
auto AddFile = [&](size_t Task, StringRef Path) {
279-
auto ReloadedBufferOrErr = MemoryBuffer::getFile(Path);
280-
if (auto EC = ReloadedBufferOrErr.getError())
281-
report_fatal_error(Twine("Can't reload cached file '") + Path + "': " +
282-
EC.message() + "\n");
283-
284-
*AddStream(Task)->OS << (*ReloadedBufferOrErr)->getBuffer();
278+
auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
279+
*AddStream(Task)->OS << MB->getBuffer();
285280
};
286281

287282
NativeObjectCache Cache;
288283
if (!CacheDir.empty())
289-
Cache = check(localCache(CacheDir, AddFile), "failed to create cache");
284+
Cache = check(localCache(CacheDir, AddBuffer), "failed to create cache");
290285

291286
check(Lto.run(AddStream, Cache), "LTO::run failed");
292287
}

0 commit comments

Comments
 (0)