summaryrefslogtreecommitdiffstats
path: root/src/deps_log.cc
diff options
context:
space:
mode:
authorNico Weber <nicolasweber@gmx.de>2013-06-14 05:11:34 (GMT)
committerNico Weber <nicolasweber@gmx.de>2013-06-14 05:19:09 (GMT)
commit13bad8d61c1a1d25328ae99daf50ac71294b8bb1 (patch)
tree4030f78908190d37f13aadb0ac901fb3f0b60ec9 /src/deps_log.cc
parent75918b84f78aaca2133f21622374019cd2ef17ee (diff)
downloadNinja-13bad8d61c1a1d25328ae99daf50ac71294b8bb1.zip
Ninja-13bad8d61c1a1d25328ae99daf50ac71294b8bb1.tar.gz
Ninja-13bad8d61c1a1d25328ae99daf50ac71294b8bb1.tar.bz2
Make sure to not write partial deps entries.
When two ninja instances run in parallel in the same build directory, one instance could write a deps entry header and a few ids, and then the other instance could write a file name in the middle of the deps header. When ninja reads this deps log on the next run, it will deserialize the file name as indices, which will cause an out-of-bounds read. (This can happen if a user runs a "compile this file" that uses ninja to compile the current buffer in an editor, and also does a full build in a terminal at the same time for example.) While running two ninja instances in parallel in the same build directory isn't a good idea, it happens to mostly work in non-deps mode: There's redundant edge execution, but nothing crashes. This is partially because the command log is line-buffered and a single log entry only consists of a single line. This change makes sure that deps entries are always written in one go, like command log entries currently are. Running two ninja binaries in parallel on the same build directory still isn't a great idea, but it's less likely to lead to crashes. See issue #595.
Diffstat (limited to 'src/deps_log.cc')
-rw-r--r--src/deps_log.cc9
1 files changed, 9 insertions, 0 deletions
diff --git a/src/deps_log.cc b/src/deps_log.cc
index 931cc77..ce9bf06 100644
--- a/src/deps_log.cc
+++ b/src/deps_log.cc
@@ -32,6 +32,11 @@
const char kFileSignature[] = "# ninjadeps\n";
const int kCurrentVersion = 1;
+// Since the size field is 2 bytes and the top bit marks deps entries, a single
+// record can be at most 32 kB. Set the buffer size to this and flush the file
+// buffer after every record to make sure records aren't written partially.
+const int kMaxBufferSize = 1 << 15;
+
DepsLog::~DepsLog() {
Close();
}
@@ -48,6 +53,7 @@ bool DepsLog::OpenForWrite(const string& path, string* err) {
*err = strerror(errno);
return false;
}
+ setvbuf(file_, NULL, _IOFBF, kMaxBufferSize);
SetCloseOnExec(fileno(file_));
// Opening a file in append mode doesn't set the file pointer to the file's
@@ -64,6 +70,7 @@ bool DepsLog::OpenForWrite(const string& path, string* err) {
return false;
}
}
+ fflush(file_);
return true;
}
@@ -124,6 +131,7 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime,
id = nodes[i]->id();
fwrite(&id, 4, 1, file_);
}
+ fflush(file_);
// Update in-memory representation.
Deps* deps = new Deps(mtime, node_count);
@@ -318,6 +326,7 @@ bool DepsLog::RecordId(Node* node) {
uint16_t size = (uint16_t)node->path().size();
fwrite(&size, 2, 1, file_);
fwrite(node->path().data(), node->path().size(), 1, file_);
+ fflush(file_);
node->set_id(nodes_.size());
nodes_.push_back(node);