// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ipc/ipc_message.h" #include "ppapi/proxy/nacl_message_scanner.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppapi_proxy_test.h" #include "ppapi/proxy/serialized_handle.h" #include "ppapi/shared_impl/host_resource.h" namespace ppapi { namespace proxy { namespace { const PP_Resource kInvalidResource = 0; const PP_Resource kFileSystem = 1; const PP_Resource kFileIO = 2; const int64_t kQuotaReservationAmount = 100; } class NaClMessageScannerTest : public PluginProxyTest { public: NaClMessageScannerTest() {} NaClMessageScanner::FileSystem* FindFileSystem( const NaClMessageScanner& scanner, PP_Resource file_system) { NaClMessageScanner::FileSystemMap::const_iterator it = scanner.file_systems_.find(file_system); return (it != scanner.file_systems_.end()) ? it->second : NULL; } NaClMessageScanner::FileIO* FindFileIO( const NaClMessageScanner& scanner, PP_Resource file_io) { NaClMessageScanner::FileIOMap::const_iterator it = scanner.files_.find(file_io); return (it != scanner.files_.end()) ? it->second : NULL; } void OpenQuotaFile(NaClMessageScanner* scanner, PP_Resource file_io, PP_Resource file_system) { std::vector unused_handles; ResourceMessageReplyParams fio_reply_params(file_io, 0); scoped_ptr new_msg_ptr; scanner->ScanMessage( PpapiPluginMsg_ResourceReply( fio_reply_params, PpapiPluginMsg_FileIO_OpenReply(file_system, 0)), PpapiPluginMsg_ResourceReply::ID, &unused_handles, &new_msg_ptr); EXPECT_FALSE(new_msg_ptr); } }; TEST_F(NaClMessageScannerTest, FileOpenClose) { NaClMessageScanner test; std::vector unused_handles; ResourceMessageCallParams fio_call_params(kFileIO, 0); ResourceMessageCallParams fs_call_params(kFileSystem, 0); ResourceMessageReplyParams fio_reply_params(kFileIO, 0); ResourceMessageReplyParams fs_reply_params(kFileSystem, 0); scoped_ptr new_msg_ptr; EXPECT_EQ(NULL, FindFileSystem(test, kFileSystem)); EXPECT_EQ(NULL, FindFileIO(test, kFileIO)); // Open a file, not in a quota file system. test.ScanMessage( PpapiPluginMsg_ResourceReply( fio_reply_params, PpapiPluginMsg_FileIO_OpenReply(kInvalidResource, 0)), PpapiPluginMsg_ResourceReply::ID, &unused_handles, &new_msg_ptr); EXPECT_FALSE(new_msg_ptr); EXPECT_FALSE(FindFileSystem(test, kFileSystem)); EXPECT_FALSE(FindFileIO(test, kFileIO)); // Open a file in a quota file system; info objects for it and its file system // should be created. OpenQuotaFile(&test, kFileIO, kFileSystem); NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem); NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO); EXPECT_TRUE(fs); EXPECT_EQ(0, fs->reserved_quota()); EXPECT_TRUE(fio); EXPECT_EQ(0, fio->max_written_offset()); const int64_t kNewFileSize = 10; fio->SetMaxWrittenOffset(kNewFileSize); // We should not be able to under-report max_written_offset when closing. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fio_call_params, PpapiHostMsg_FileIO_Close(FileGrowth(0, 0))), &new_msg_ptr); EXPECT_TRUE(new_msg_ptr); ResourceMessageCallParams call_params; IPC::Message nested_msg; FileGrowth file_growth; EXPECT_TRUE(UnpackMessage( *new_msg_ptr, &call_params, &nested_msg) && UnpackMessage( nested_msg, &file_growth)); new_msg_ptr.reset(); EXPECT_EQ(kNewFileSize, file_growth.max_written_offset); EXPECT_FALSE(FindFileIO(test, kFileIO)); // Reopen the file. OpenQuotaFile(&test, kFileIO, kFileSystem); fio = FindFileIO(test, kFileIO); fio->SetMaxWrittenOffset(kNewFileSize); // Close with correct max_written_offset. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fio_call_params, PpapiHostMsg_FileIO_Close(FileGrowth(kNewFileSize, 0))), &new_msg_ptr); EXPECT_FALSE(new_msg_ptr); EXPECT_FALSE(FindFileIO(test, kFileIO)); // Destroy file system. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fs_call_params, PpapiHostMsg_ResourceDestroyed(kFileSystem)), &new_msg_ptr); EXPECT_FALSE(FindFileSystem(test, kFileSystem)); } TEST_F(NaClMessageScannerTest, QuotaAuditing) { NaClMessageScanner test; std::vector unused_handles; ResourceMessageCallParams fio_call_params(kFileIO, 0); ResourceMessageCallParams fs_call_params(kFileSystem, 0); ResourceMessageReplyParams fio_reply_params(kFileIO, 0); ResourceMessageReplyParams fs_reply_params(kFileSystem, 0); scoped_ptr new_msg_ptr; OpenQuotaFile(&test, kFileIO, kFileSystem); NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem); NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO); EXPECT_TRUE(fs); EXPECT_EQ(0, fs->reserved_quota()); EXPECT_TRUE(fio); EXPECT_EQ(0, fio->max_written_offset()); // Without reserving quota, we should not be able to grow the file. EXPECT_FALSE(fio->Grow(1)); EXPECT_EQ(0, fs->reserved_quota()); EXPECT_EQ(0, fio->max_written_offset()); // Receive reserved quota, and updated file sizes. const int64_t kNewFileSize = 10; FileSizeMap file_sizes; file_sizes[kFileIO] = kNewFileSize; test.ScanMessage( PpapiPluginMsg_ResourceReply( fs_reply_params, PpapiPluginMsg_FileSystem_ReserveQuotaReply( kQuotaReservationAmount, file_sizes)), PpapiPluginMsg_ResourceReply::ID, &unused_handles, &new_msg_ptr); EXPECT_FALSE(new_msg_ptr); EXPECT_EQ(kQuotaReservationAmount, fs->reserved_quota()); EXPECT_EQ(kNewFileSize, fio->max_written_offset()); // We should be able to grow the file within quota. EXPECT_TRUE(fio->Grow(1)); EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota()); EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset()); // We should not be able to grow the file over quota. EXPECT_FALSE(fio->Grow(kQuotaReservationAmount)); EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota()); EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset()); // Plugin should not under-report max written offsets when reserving quota. file_sizes[kFileIO] = 0; // should be kNewFileSize + 1. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fio_call_params, PpapiHostMsg_FileSystem_ReserveQuota( kQuotaReservationAmount, FileSizeMapToFileGrowthMapForTesting(file_sizes))), &new_msg_ptr); EXPECT_TRUE(new_msg_ptr); ResourceMessageCallParams call_params; IPC::Message nested_msg; int64_t amount = 0; FileGrowthMap new_file_growths; EXPECT_TRUE(UnpackMessage( *new_msg_ptr, &call_params, &nested_msg) && UnpackMessage( nested_msg, &amount, &new_file_growths)); new_msg_ptr.reset(); EXPECT_EQ(kQuotaReservationAmount, amount); EXPECT_EQ(kNewFileSize + 1, new_file_growths[kFileIO].max_written_offset); } TEST_F(NaClMessageScannerTest, SetLength) { NaClMessageScanner test; std::vector unused_handles; ResourceMessageCallParams fio_call_params(kFileIO, 0); ResourceMessageCallParams fs_call_params(kFileSystem, 0); ResourceMessageReplyParams fio_reply_params(kFileIO, 0); ResourceMessageReplyParams fs_reply_params(kFileSystem, 0); scoped_ptr new_msg_ptr; OpenQuotaFile(&test, kFileIO, kFileSystem); NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem); NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO); // Receive reserved quota, and updated file sizes. const int64_t kNewFileSize = 10; FileSizeMap file_sizes; file_sizes[kFileIO] = 0; test.ScanMessage( PpapiPluginMsg_ResourceReply( fs_reply_params, PpapiPluginMsg_FileSystem_ReserveQuotaReply( kQuotaReservationAmount, file_sizes)), PpapiPluginMsg_ResourceReply::ID, &unused_handles, &new_msg_ptr); // We should be able to SetLength within quota. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fio_call_params, PpapiHostMsg_FileIO_SetLength(kNewFileSize)), &new_msg_ptr); EXPECT_FALSE(new_msg_ptr); EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota()); EXPECT_EQ(kNewFileSize, fio->max_written_offset()); // We shouldn't be able to SetLength beyond quota. The message should be // rewritten to fail with length == -1. test.ScanUntrustedMessage( PpapiHostMsg_ResourceCall( fio_call_params, PpapiHostMsg_FileIO_SetLength(kQuotaReservationAmount + 1)), &new_msg_ptr); EXPECT_TRUE(new_msg_ptr); ResourceMessageCallParams call_params; IPC::Message nested_msg; int64_t length = 0; EXPECT_TRUE(UnpackMessage( *new_msg_ptr, &call_params, &nested_msg) && UnpackMessage( nested_msg, &length)); new_msg_ptr.reset(); EXPECT_EQ(-1, length); EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota()); EXPECT_EQ(kNewFileSize, fio->max_written_offset()); } } // namespace proxy } // namespace ppapi