1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ipc/ipc_message.h"
6#include "ppapi/proxy/nacl_message_scanner.h"
7#include "ppapi/proxy/ppapi_messages.h"
8#include "ppapi/proxy/ppapi_proxy_test.h"
9#include "ppapi/proxy/serialized_handle.h"
10#include "ppapi/shared_impl/host_resource.h"
11
12namespace ppapi {
13namespace proxy {
14
15namespace {
16const PP_Resource kInvalidResource = 0;
17const PP_Resource kFileSystem = 1;
18const PP_Resource kFileIO = 2;
19const int64_t kQuotaReservationAmount = 100;
20}
21
22class NaClMessageScannerTest : public PluginProxyTest {
23 public:
24  NaClMessageScannerTest() {}
25
26  NaClMessageScanner::FileSystem* FindFileSystem(
27      const NaClMessageScanner& scanner,
28      PP_Resource file_system) {
29    NaClMessageScanner::FileSystemMap::const_iterator it =
30        scanner.file_systems_.find(file_system);
31    return (it != scanner.file_systems_.end()) ? it->second : NULL;
32  }
33
34  NaClMessageScanner::FileIO* FindFileIO(
35      const NaClMessageScanner& scanner,
36      PP_Resource file_io) {
37    NaClMessageScanner::FileIOMap::const_iterator it =
38        scanner.files_.find(file_io);
39    return (it != scanner.files_.end()) ? it->second : NULL;
40  }
41
42  void OpenQuotaFile(NaClMessageScanner* scanner,
43                     PP_Resource file_io,
44                     PP_Resource file_system) {
45    std::vector<SerializedHandle> unused_handles;
46    ResourceMessageReplyParams fio_reply_params(file_io, 0);
47    scoped_ptr<IPC::Message> new_msg_ptr;
48    scanner->ScanMessage(
49        PpapiPluginMsg_ResourceReply(
50            fio_reply_params,
51            PpapiPluginMsg_FileIO_OpenReply(file_system, 0)),
52        PpapiPluginMsg_ResourceReply::ID,
53        &unused_handles,
54        &new_msg_ptr);
55    EXPECT_FALSE(new_msg_ptr);
56  }
57};
58
59TEST_F(NaClMessageScannerTest, FileOpenClose) {
60  NaClMessageScanner test;
61  std::vector<SerializedHandle> unused_handles;
62  ResourceMessageCallParams fio_call_params(kFileIO, 0);
63  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
64  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
65  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
66  scoped_ptr<IPC::Message> new_msg_ptr;
67
68  EXPECT_EQ(NULL, FindFileSystem(test, kFileSystem));
69  EXPECT_EQ(NULL, FindFileIO(test, kFileIO));
70
71  // Open a file, not in a quota file system.
72  test.ScanMessage(
73      PpapiPluginMsg_ResourceReply(
74          fio_reply_params,
75          PpapiPluginMsg_FileIO_OpenReply(kInvalidResource, 0)),
76      PpapiPluginMsg_ResourceReply::ID,
77      &unused_handles,
78      &new_msg_ptr);
79  EXPECT_FALSE(new_msg_ptr);
80  EXPECT_FALSE(FindFileSystem(test, kFileSystem));
81  EXPECT_FALSE(FindFileIO(test, kFileIO));
82
83  // Open a file in a quota file system; info objects for it and its file system
84  // should be created.
85  OpenQuotaFile(&test, kFileIO, kFileSystem);
86  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
87  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
88  EXPECT_TRUE(fs);
89  EXPECT_EQ(0, fs->reserved_quota());
90  EXPECT_TRUE(fio);
91  EXPECT_EQ(0, fio->max_written_offset());
92
93  const int64_t kNewFileSize = 10;
94  fio->SetMaxWrittenOffset(kNewFileSize);
95
96  // We should not be able to under-report max_written_offset when closing.
97  test.ScanUntrustedMessage(
98      PpapiHostMsg_ResourceCall(
99          fio_call_params,
100          PpapiHostMsg_FileIO_Close(FileGrowth(0, 0))),
101      &new_msg_ptr);
102  EXPECT_TRUE(new_msg_ptr);
103  ResourceMessageCallParams call_params;
104  IPC::Message nested_msg;
105  FileGrowth file_growth;
106  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
107                  *new_msg_ptr, &call_params, &nested_msg) &&
108              UnpackMessage<PpapiHostMsg_FileIO_Close>(
109                  nested_msg, &file_growth));
110  new_msg_ptr.reset();
111  EXPECT_EQ(kNewFileSize, file_growth.max_written_offset);
112  EXPECT_FALSE(FindFileIO(test, kFileIO));
113
114  // Reopen the file.
115  OpenQuotaFile(&test, kFileIO, kFileSystem);
116  fio = FindFileIO(test, kFileIO);
117  fio->SetMaxWrittenOffset(kNewFileSize);
118
119  // Close with correct max_written_offset.
120  test.ScanUntrustedMessage(
121      PpapiHostMsg_ResourceCall(
122          fio_call_params,
123          PpapiHostMsg_FileIO_Close(FileGrowth(kNewFileSize, 0))),
124      &new_msg_ptr);
125  EXPECT_FALSE(new_msg_ptr);
126  EXPECT_FALSE(FindFileIO(test, kFileIO));
127
128  // Destroy file system.
129  test.ScanUntrustedMessage(
130      PpapiHostMsg_ResourceCall(
131          fs_call_params,
132          PpapiHostMsg_ResourceDestroyed(kFileSystem)),
133      &new_msg_ptr);
134  EXPECT_FALSE(FindFileSystem(test, kFileSystem));
135}
136
137TEST_F(NaClMessageScannerTest, QuotaAuditing) {
138  NaClMessageScanner test;
139  std::vector<SerializedHandle> unused_handles;
140  ResourceMessageCallParams fio_call_params(kFileIO, 0);
141  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
142  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
143  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
144  scoped_ptr<IPC::Message> new_msg_ptr;
145
146  OpenQuotaFile(&test, kFileIO, kFileSystem);
147  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
148  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
149  EXPECT_TRUE(fs);
150  EXPECT_EQ(0, fs->reserved_quota());
151  EXPECT_TRUE(fio);
152  EXPECT_EQ(0, fio->max_written_offset());
153
154  // Without reserving quota, we should not be able to grow the file.
155  EXPECT_FALSE(fio->Grow(1));
156  EXPECT_EQ(0, fs->reserved_quota());
157  EXPECT_EQ(0, fio->max_written_offset());
158
159  // Receive reserved quota, and updated file sizes.
160  const int64_t kNewFileSize = 10;
161  FileSizeMap file_sizes;
162  file_sizes[kFileIO] = kNewFileSize;
163  test.ScanMessage(
164      PpapiPluginMsg_ResourceReply(
165          fs_reply_params,
166          PpapiPluginMsg_FileSystem_ReserveQuotaReply(
167              kQuotaReservationAmount,
168              file_sizes)),
169      PpapiPluginMsg_ResourceReply::ID,
170      &unused_handles,
171      &new_msg_ptr);
172  EXPECT_FALSE(new_msg_ptr);
173  EXPECT_EQ(kQuotaReservationAmount, fs->reserved_quota());
174  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
175
176  // We should be able to grow the file within quota.
177  EXPECT_TRUE(fio->Grow(1));
178  EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
179  EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
180
181  // We should not be able to grow the file over quota.
182  EXPECT_FALSE(fio->Grow(kQuotaReservationAmount));
183  EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
184  EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
185
186  // Plugin should not under-report max written offsets when reserving quota.
187  file_sizes[kFileIO] = 0;  // should be kNewFileSize + 1.
188  test.ScanUntrustedMessage(
189      PpapiHostMsg_ResourceCall(
190          fio_call_params,
191          PpapiHostMsg_FileSystem_ReserveQuota(
192              kQuotaReservationAmount,
193              FileSizeMapToFileGrowthMapForTesting(file_sizes))),
194      &new_msg_ptr);
195  EXPECT_TRUE(new_msg_ptr);
196  ResourceMessageCallParams call_params;
197  IPC::Message nested_msg;
198  int64_t amount = 0;
199  FileGrowthMap new_file_growths;
200  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
201                  *new_msg_ptr, &call_params, &nested_msg) &&
202              UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
203                  nested_msg, &amount, &new_file_growths));
204  new_msg_ptr.reset();
205  EXPECT_EQ(kQuotaReservationAmount, amount);
206  EXPECT_EQ(kNewFileSize + 1, new_file_growths[kFileIO].max_written_offset);
207}
208
209TEST_F(NaClMessageScannerTest, SetLength) {
210  NaClMessageScanner test;
211  std::vector<SerializedHandle> unused_handles;
212  ResourceMessageCallParams fio_call_params(kFileIO, 0);
213  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
214  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
215  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
216  scoped_ptr<IPC::Message> new_msg_ptr;
217
218  OpenQuotaFile(&test, kFileIO, kFileSystem);
219  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
220  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
221
222  // Receive reserved quota, and updated file sizes.
223  const int64_t kNewFileSize = 10;
224  FileSizeMap file_sizes;
225  file_sizes[kFileIO] = 0;
226  test.ScanMessage(
227      PpapiPluginMsg_ResourceReply(
228          fs_reply_params,
229          PpapiPluginMsg_FileSystem_ReserveQuotaReply(
230              kQuotaReservationAmount,
231              file_sizes)),
232      PpapiPluginMsg_ResourceReply::ID,
233      &unused_handles,
234      &new_msg_ptr);
235
236  // We should be able to SetLength within quota.
237  test.ScanUntrustedMessage(
238      PpapiHostMsg_ResourceCall(
239          fio_call_params,
240          PpapiHostMsg_FileIO_SetLength(kNewFileSize)),
241      &new_msg_ptr);
242  EXPECT_FALSE(new_msg_ptr);
243  EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
244  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
245
246  // We shouldn't be able to SetLength beyond quota. The message should be
247  // rewritten to fail with length == -1.
248  test.ScanUntrustedMessage(
249      PpapiHostMsg_ResourceCall(
250          fio_call_params,
251          PpapiHostMsg_FileIO_SetLength(kQuotaReservationAmount + 1)),
252      &new_msg_ptr);
253  EXPECT_TRUE(new_msg_ptr);
254  ResourceMessageCallParams call_params;
255  IPC::Message nested_msg;
256  int64_t length = 0;
257  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
258                  *new_msg_ptr, &call_params, &nested_msg) &&
259              UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
260                  nested_msg, &length));
261  new_msg_ptr.reset();
262  EXPECT_EQ(-1, length);
263  EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
264  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
265}
266
267}  // namespace proxy
268}  // namespace ppapi
269