Import Cobalt 2.15147
Change-Id: I9a11d41d571fb25d2a0f9b9fd386d661f7d4ce71
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index b3eeb13..159bca5 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-13610
\ No newline at end of file
+15147
\ No newline at end of file
diff --git a/src/cobalt/storage/storage_manager.cc b/src/cobalt/storage/storage_manager.cc
index decba88..79a5c8f 100644
--- a/src/cobalt/storage/storage_manager.cc
+++ b/src/cobalt/storage/storage_manager.cc
@@ -275,11 +275,9 @@
sizeof(VirtualFileSystem::SerializedHeader));
}
- if (VirtualFileSystem::GetHeaderVersion(header) == -1) {
+ if (!vfs_->Deserialize(&raw_bytes[0], buffer_size)) {
VirtualFile* vf = vfs_->Open(kDefaultSaveFile);
vf->Write(&raw_bytes[0], buffer_size, 0 /* offset */);
- } else {
- vfs_->Deserialize(&raw_bytes[0], buffer_size);
}
}
}
diff --git a/src/cobalt/storage/virtual_file.cc b/src/cobalt/storage/virtual_file.cc
index ee738da..64904c2 100644
--- a/src/cobalt/storage/virtual_file.cc
+++ b/src/cobalt/storage/virtual_file.cc
@@ -27,60 +27,74 @@
// We must use forward declarations to be able to specify the
// WARN_UNUSED_RESULT attribute.
-// Read size bytes from buffer into dest. Return # of bytes read.
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) WARN_UNUSED_RESULT;
+// Read size bytes from buffer into dest. Updates buffer and buffer_remaining
+// on success.
+// Returns true on success, false on buffer overrun
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) WARN_UNUSED_RESULT;
// Write size bytes from source into buffer. Return # of bytes written.
-int WriteBuffer(uint8* buffer, const uint8* source, int size,
- bool dry_run) WARN_UNUSED_RESULT;
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) WARN_UNUSED_RESULT;
-int ReadBuffer(uint8* dest, const uint8* buffer, int size) {
- memcpy(dest, buffer, static_cast<size_t>(size));
- return size;
+bool ReadBuffer(uint8* dest, const uint8** buffer, size_t size,
+ size_t* buffer_remaining) {
+ if (size > *buffer_remaining) {
+ return false;
+ }
+ memcpy(dest, *buffer, size);
+ *buffer_remaining -= size;
+ *buffer += size;
+ return true;
}
-int WriteBuffer(uint8* buffer, const uint8* source, int size, bool dry_run) {
+size_t WriteBuffer(uint8* buffer, const uint8* source, size_t size,
+ bool dry_run) {
if (!dry_run) {
- memcpy(buffer, source, static_cast<size_t>(size));
+ memcpy(buffer, source, size);
}
return size;
}
} // namespace
-VirtualFile::VirtualFile(const std::string& name) : size_(0), name_(name) {}
+VirtualFile::VirtualFile(const std::string& name) : name_(name) {}
VirtualFile::~VirtualFile() {}
-int VirtualFile::Read(void* dest, int bytes, int offset) const {
- DCHECK_GE(offset, 0);
- if (offset > size_) {
+int VirtualFile::Read(void* dest, int bytes_in, int offset_in) const {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t offset = static_cast<size_t>(offset_in);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t size = buffer_.size();
+ if (offset > size) {
return 0;
}
- int bytes_to_read = std::min(size_ - offset, bytes);
+ size_t bytes_to_read = std::min(static_cast<size_t>(size - offset), bytes);
if (bytes_to_read == 0) {
return 0;
}
- memcpy(dest, &buffer_[static_cast<size_t>(offset)],
- static_cast<size_t>(bytes_to_read));
- return bytes_to_read;
+ memcpy(dest, &buffer_[offset], bytes_to_read);
+ return static_cast<int>(bytes_to_read);
}
-int VirtualFile::Write(const void* source, int bytes, int offset) {
- DCHECK_GE(offset, 0);
- DCHECK_GE(bytes, 0);
- if (static_cast<int>(buffer_.size()) < offset + bytes) {
- buffer_.resize(static_cast<size_t>(offset + bytes));
+int VirtualFile::Write(const void* source, int bytes_in, int offset_in) {
+ DCHECK_GE(offset_in, 0);
+ DCHECK_GE(bytes_in, 0);
+ size_t bytes = static_cast<size_t>(bytes_in);
+ size_t offset = static_cast<size_t>(offset_in);
+ if (buffer_.size() < offset + bytes) {
+ buffer_.resize(offset + bytes);
}
- memcpy(&buffer_[static_cast<size_t>(offset)], source,
- static_cast<size_t>(bytes));
- size_ = std::max(size_, offset + bytes);
- return bytes;
+ memcpy(&buffer_[offset], source, bytes);
+ return bytes_in;
}
int VirtualFile::Truncate(int size) {
- size_ = std::min(size_, size);
- return size_;
+ size_t newsize = std::min(buffer_.size(), static_cast<size_t>(size));
+ buffer_.resize(newsize);
+ return static_cast<int>(newsize);
}
int VirtualFile::Serialize(uint8* dest, bool dry_run) {
@@ -96,29 +110,36 @@
// Write the filename
cur_buffer +=
WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(name_.c_str()),
- static_cast<int>(name_length), dry_run);
+ static_cast<size_t>(name_length), dry_run);
// NOTE: Ensure the file size is 64-bit for compatibility
// with any existing serialized files.
- uint64 file_size = static_cast<uint64>(size_);
+ uint64 file_size = static_cast<uint64>(buffer_.size());
// Write the file contents size
cur_buffer +=
WriteBuffer(cur_buffer, reinterpret_cast<const uint8*>(&file_size),
sizeof(file_size), dry_run);
// Write the file contents
- cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], size_, dry_run);
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ cur_buffer += WriteBuffer(cur_buffer, &buffer_[0], buffer_.size(), dry_run);
+ }
// Return the number of bytes written
return static_cast<int>(std::distance(dest, cur_buffer));
}
-int VirtualFile::Deserialize(const uint8* source) {
+int VirtualFile::Deserialize(const uint8* source, size_t buffer_remaining) {
// Read in filename length
const uint8* cur_buffer = source;
uint64 name_length;
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&name_length), cur_buffer,
- sizeof(name_length));
+ bool success = ReadBuffer(reinterpret_cast<uint8*>(&name_length), &cur_buffer,
+ sizeof(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
if (name_length >= kMaxVfsPathname) {
DLOG(ERROR) << "Filename was longer than the maximum allowed.";
@@ -126,19 +147,34 @@
}
// Read in filename
char name[kMaxVfsPathname];
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(name), cur_buffer,
- static_cast<int>(name_length));
+ success = ReadBuffer(reinterpret_cast<uint8*>(name), &cur_buffer,
+ static_cast<size_t>(name_length), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
name_.assign(name, name_length);
// Read in file contents size.
uint64 file_size;
- cur_buffer += ReadBuffer(reinterpret_cast<uint8*>(&file_size), cur_buffer,
- sizeof(file_size));
- size_ = static_cast<int>(file_size);
-
+ success = ReadBuffer(reinterpret_cast<uint8*>(&file_size), &cur_buffer,
+ sizeof(file_size), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
// Read in the file contents
- buffer_.resize(static_cast<size_t>(size_));
- cur_buffer += ReadBuffer(&buffer_[0], cur_buffer, size_);
+ buffer_.resize(static_cast<size_t>(file_size));
+
+ if (!buffer_.empty()) {
+ // std::vector does not define access to underlying array when empty
+ success =
+ ReadBuffer(&buffer_[0], &cur_buffer, buffer_.size(), &buffer_remaining);
+ if (!success) {
+ DLOG(ERROR) << "Buffer overrun";
+ return -1;
+ }
+ }
// Return the number of bytes read
return static_cast<int>(std::distance(source, cur_buffer));
diff --git a/src/cobalt/storage/virtual_file.h b/src/cobalt/storage/virtual_file.h
index 5741fd0..f5a9aec 100644
--- a/src/cobalt/storage/virtual_file.h
+++ b/src/cobalt/storage/virtual_file.h
@@ -41,7 +41,7 @@
int Read(void* dest, int bytes, int offset) const;
int Write(const void* source, int bytes, int offset);
int Truncate(int size);
- int Size() const { return size_; }
+ int Size() const { return static_cast<int>(buffer_.size()); }
private:
explicit VirtualFile(const std::string& name);
@@ -49,10 +49,12 @@
// Returns the number of bytes written
int Serialize(uint8* dest, const bool dry_run);
- int Deserialize(const uint8* source);
+ // Deserializes a file, returning the size of the file or
+ // < 0 on error.
+ // |buffer_remaining| is the maximum size of |source|
+ int Deserialize(const uint8* source, size_t buffer_remaining);
std::vector<uint8> buffer_;
- int size_;
std::string name_;
diff --git a/src/cobalt/storage/virtual_file_system.cc b/src/cobalt/storage/virtual_file_system.cc
index 70e1556..9951df6 100644
--- a/src/cobalt/storage/virtual_file_system.cc
+++ b/src/cobalt/storage/virtual_file_system.cc
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "cobalt/storage/virtual_file_system.h"
#include "base/logging.h"
@@ -124,23 +123,27 @@
return bytes_written;
}
-void VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size) {
+bool VirtualFileSystem::Deserialize(const uint8* buffer, int buffer_size_in) {
+ const uint8* caller_buffer = buffer;
+
base::AutoLock lock(file_table_lock_);
ClearFileTable();
- if (buffer_size < 0) {
- DLOG(ERROR) << "Buffer size must be positive: "
- << buffer_size << " < 0.";
- return;
+ if (buffer_size_in < 0) {
+ DLOG(ERROR) << "Buffer size must be positive: " << buffer_size_in
+ << " < 0.";
+ return false;
}
+ size_t buffer_size = static_cast<size_t>(buffer_size_in);
+
// The size of the buffer must be checked before copying the beginning of it
// into a SerializedHeader.
- if (static_cast<size_t>(buffer_size) < sizeof(SerializedHeader)) {
+ if (buffer_size < sizeof(SerializedHeader)) {
DLOG(ERROR) << "Buffer size " << buffer_size
<< " is too small to contain a SerializedHeader of size "
<< sizeof(SerializedHeader) << "; operation aborted.";
- return;
+ return false;
}
// Read in expected number of files
@@ -152,23 +155,36 @@
if (buffer_version != 0) {
// Note: We would handle old versions here, if necessary.
DLOG(ERROR) << "Attempted to load a different version; operation aborted.";
- return;
- } else if (header.file_size != buffer_size) {
+ return false;
+ } else if (static_cast<size_t>(header.file_size) != buffer_size) {
DLOG(ERROR) << "Buffer size mismatch: " << header.file_size
<< " != " << buffer_size;
- return;
+ return false;
}
for (int i = 0; i < header.file_count; ++i) {
+ size_t buffer_used = static_cast<size_t>(buffer - caller_buffer);
+ if (buffer_size < buffer_used) {
+ DLOG(ERROR) << "Buffer overrun deserializing files";
+ ClearFileTable();
+ return false;
+ }
+ size_t buffer_remaining = buffer_size - buffer_used;
+
VirtualFile* file = new VirtualFile("");
- int bytes = file->Deserialize(buffer);
+
+ int bytes = file->Deserialize(buffer, buffer_remaining);
if (bytes > 0) {
buffer += bytes;
table_[file->name_] = file;
} else {
DLOG(WARNING) << "Failed to deserialize virtual file system.";
+ delete file;
+ ClearFileTable();
+ return false;
}
}
+ return true;
}
void VirtualFileSystem::ClearFileTable() {
diff --git a/src/cobalt/storage/virtual_file_system.h b/src/cobalt/storage/virtual_file_system.h
index e0b69b0..bef9a4c 100644
--- a/src/cobalt/storage/virtual_file_system.h
+++ b/src/cobalt/storage/virtual_file_system.h
@@ -59,7 +59,8 @@
int Serialize(uint8* buffer, bool dry_run);
// Deserializes a file system from a memory buffer.
- void Deserialize(const uint8* buffer, int buffer_size);
+ // Returns false on failure.
+ bool Deserialize(const uint8* buffer, int buffer_size);
// Simple file open. Will create a file if it does not exist, and files are
// always readable and writable.
diff --git a/src/cobalt/storage/virtual_file_system_test.cc b/src/cobalt/storage/virtual_file_system_test.cc
index e507516..de36f6f 100644
--- a/src/cobalt/storage/virtual_file_system_test.cc
+++ b/src/cobalt/storage/virtual_file_system_test.cc
@@ -21,6 +21,8 @@
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "starboard/client_porting/poem/string_poem.h"
+
namespace cobalt {
namespace storage {
@@ -102,6 +104,32 @@
EXPECT_EQ(4, file->Size());
}
+TEST_F(VirtualFileSystemTest, Truncate) {
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ ASSERT_TRUE(file);
+ EXPECT_EQ(0, file->Size());
+
+ const char* data = "test";
+ char file_contents[4];
+ // Write a few bytes of random data.
+ file->Write(data, 4, 0);
+ EXPECT_EQ(4, file->Size());
+ int bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(4, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(3);
+ EXPECT_EQ(3, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(3, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data, static_cast<size_t>(bytes)));
+
+ file->Truncate(0);
+ EXPECT_EQ(0, file->Size());
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(0, bytes);
+}
+
TEST_F(VirtualFileSystemTest, SerializeDeserialize) {
// Create a few files and write some data
VirtualFile* file = vfs_.Open("file1.tmp");
@@ -116,6 +144,58 @@
int data2_size = 4;
file->Write(data2, data2_size, 0);
+ file = vfs_.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data3[] = "";
+ int data3_size = 0;
+ file->Write(data3, data3_size, 0);
+
+ // First perform a dry run to figure out how much space we need.
+ int bytes = vfs_.Serialize(NULL, true /*dry run*/);
+ scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
+
+ // Now serialize and deserialize
+ vfs_.Serialize(buffer.get(), false /*dry run*/);
+
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ ASSERT_TRUE(new_vfs.Deserialize(buffer.get(), bytes));
+
+ // Make sure the new vfs contains all the expected data.
+ char file_contents[VirtualFile::kMaxVfsPathname];
+ file = new_vfs.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data1_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data2_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+
+ file = new_vfs.Open("file3.tmp");
+ EXPECT_TRUE(file != NULL);
+ bytes = file->Read(file_contents, sizeof(file_contents), 0);
+ EXPECT_EQ(data3_size, bytes);
+ EXPECT_EQ(0, memcmp(file_contents, data3, static_cast<size_t>(bytes)));
+}
+
+TEST_F(VirtualFileSystemTest, DeserializeTruncated) {
+ // Create a few files and write some data
+ VirtualFile* file = vfs_.Open("file1.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data1[] = "abc";
+ int data1_size = 3;
+ file->Write(data1, data1_size, 0);
+
+ file = vfs_.Open("file2.tmp");
+ EXPECT_TRUE(file != NULL);
+ const char data2[] = "defg";
+ int data2_size = 4;
+ file->Write(data2, data2_size, 0);
+
// First perform a dry run to figure out how much space we need.
int bytes = vfs_.Serialize(NULL, true /*dry run*/);
scoped_array<uint8> buffer(new uint8[static_cast<size_t>(bytes)]);
@@ -123,23 +203,39 @@
// Now serialize and deserialize
vfs_.Serialize(buffer.get(), false /*dry run*/);
- // Deserialize the data into a new vfs
- VirtualFileSystem new_vfs;
- new_vfs.Deserialize(buffer.get(), bytes);
+ for (int i = 1; i < bytes; i++) {
+ // Corrupt the header
+ VirtualFileSystem::SerializedHeader header;
+ memcpy(&header, buffer.get(), sizeof(header));
+ header.file_size = header.file_size - i;
+ memcpy(buffer.get(), &header, sizeof(header));
- // Make sure the new vfs contains all the expected data.
- char file_contents[VirtualFile::kMaxVfsPathname];
- file = new_vfs.Open("file1.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data1_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data1, static_cast<size_t>(bytes)));
+ // Deserialize the data into a new vfs
+ VirtualFileSystem new_vfs;
+ EXPECT_FALSE(new_vfs.Deserialize(buffer.get(), bytes - i));
+ }
+}
- file = new_vfs.Open("file2.tmp");
- EXPECT_TRUE(file != NULL);
- bytes = file->Read(file_contents, sizeof(file_contents), 0);
- EXPECT_EQ(data2_size, bytes);
- EXPECT_EQ(0, memcmp(file_contents, data2, static_cast<size_t>(bytes)));
+TEST_F(VirtualFileSystemTest, DeserializeBadData) {
+ scoped_array<uint8> buffer(new uint8[0]);
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 0));
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), -1));
+ buffer.reset(new uint8[1]);
+ EXPECT_EQ(false, vfs_.Deserialize(buffer.get(), 1));
+ buffer.reset(new uint8[sizeof(VirtualFileSystem::SerializedHeader)]);
+ VirtualFileSystem::SerializedHeader header = {};
+ header.version = -1;
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_EQ(false,
+ vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ memcpy(&(header.version), "SAV0", sizeof(header.version));
+ header.file_size = sizeof(VirtualFileSystem::SerializedHeader);
+ memcpy(buffer.get(), &header, sizeof(header));
+ EXPECT_EQ(true,
+ vfs_.Deserialize(buffer.get(),
+ sizeof(VirtualFileSystem::SerializedHeader)));
+ ASSERT_EQ(0, vfs_.ListFiles().size());
}
TEST_F(VirtualFileSystemTest, GetHeaderVersion) {