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  uint32 FindPendingSyncMessage(
27      const NaClMessageScanner& scanner,
28      const IPC::Message& msg) {
29    int msg_id = IPC::SyncMessage::GetMessageId(msg);
30    std::map<int, uint32>::const_iterator it =
31        scanner.pending_sync_msgs_.find(msg_id);
32    // O can signal that no message was found.
33    return (it != scanner.pending_sync_msgs_.end()) ? it->second : 0;
34  }
35
36  NaClMessageScanner::FileSystem* FindFileSystem(
37      const NaClMessageScanner& scanner,
38      PP_Resource file_system) {
39    NaClMessageScanner::FileSystemMap::const_iterator it =
40        scanner.file_systems_.find(file_system);
41    return (it != scanner.file_systems_.end()) ? it->second : NULL;
42  }
43
44  NaClMessageScanner::FileIO* FindFileIO(
45      const NaClMessageScanner& scanner,
46      PP_Resource file_io) {
47    NaClMessageScanner::FileIOMap::const_iterator it =
48        scanner.files_.find(file_io);
49    return (it != scanner.files_.end()) ? it->second : NULL;
50  }
51
52  void OpenQuotaFile(NaClMessageScanner* scanner,
53                     PP_Resource file_io,
54                     PP_Resource file_system) {
55    std::vector<SerializedHandle> unused_handles;
56    ResourceMessageReplyParams fio_reply_params(file_io, 0);
57    scoped_ptr<IPC::Message> new_msg_ptr;
58    scanner->ScanMessage(
59      PpapiPluginMsg_ResourceReply(
60          fio_reply_params,
61          PpapiPluginMsg_FileIO_OpenReply(file_system, 0)),
62      &unused_handles,
63      &new_msg_ptr);
64    EXPECT_FALSE(new_msg_ptr);
65  }
66};
67
68TEST_F(NaClMessageScannerTest, SyncMessageAndReply) {
69  NaClMessageScanner test;
70  ppapi::proxy::SerializedHandle handle(
71      ppapi::proxy::SerializedHandle::SHARED_MEMORY);
72  int id = -1;
73  IPC::Message msg =
74      PpapiHostMsg_PPBGraphics3D_CreateTransferBuffer(
75          ppapi::API_ID_PPB_GRAPHICS_3D,
76          HostResource(),
77          4096,  // size
78          &id,
79          &handle);
80  scoped_ptr<IPC::Message> new_msg_ptr;
81  EXPECT_NE(msg.type(), FindPendingSyncMessage(test, msg));
82  test.ScanUntrustedMessage(msg, &new_msg_ptr);
83  EXPECT_FALSE(new_msg_ptr);
84  EXPECT_EQ(msg.type(), FindPendingSyncMessage(test, msg));
85
86  // TODO(bbudge) Figure out how to put together a sync reply message.
87}
88
89TEST_F(NaClMessageScannerTest, FileOpenClose) {
90  NaClMessageScanner test;
91  std::vector<SerializedHandle> unused_handles;
92  ResourceMessageCallParams fio_call_params(kFileIO, 0);
93  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
94  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
95  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
96  scoped_ptr<IPC::Message> new_msg_ptr;
97
98  EXPECT_EQ(NULL, FindFileSystem(test, kFileSystem));
99  EXPECT_EQ(NULL, FindFileIO(test, kFileIO));
100
101  // Open a file, not in a quota file system.
102  test.ScanMessage(
103      PpapiPluginMsg_ResourceReply(
104          fio_reply_params,
105          PpapiPluginMsg_FileIO_OpenReply(kInvalidResource, 0)),
106      &unused_handles,
107      &new_msg_ptr);
108  EXPECT_FALSE(new_msg_ptr);
109  EXPECT_FALSE(FindFileSystem(test, kFileSystem));
110  EXPECT_FALSE(FindFileIO(test, kFileIO));
111
112  // Open a file in a quota file system; info objects for it and its file system
113  // should be created.
114  OpenQuotaFile(&test, kFileIO, kFileSystem);
115  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
116  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
117  EXPECT_TRUE(fs);
118  EXPECT_EQ(0, fs->reserved_quota());
119  EXPECT_TRUE(fio);
120  EXPECT_EQ(0, fio->max_written_offset());
121
122  const int64_t kNewFileSize = 10;
123  fio->SetMaxWrittenOffset(kNewFileSize);
124
125  // We should not be able to under-report max_written_offset when closing.
126  test.ScanUntrustedMessage(
127      PpapiHostMsg_ResourceCall(
128          fio_call_params,
129          PpapiHostMsg_FileIO_Close(FileGrowth(0, 0))),
130      &new_msg_ptr);
131  EXPECT_TRUE(new_msg_ptr);
132  ResourceMessageCallParams call_params;
133  IPC::Message nested_msg;
134  FileGrowth file_growth;
135  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
136                  *new_msg_ptr, &call_params, &nested_msg) &&
137              UnpackMessage<PpapiHostMsg_FileIO_Close>(
138                  nested_msg, &file_growth));
139  new_msg_ptr.reset();
140  EXPECT_EQ(kNewFileSize, file_growth.max_written_offset);
141  EXPECT_FALSE(FindFileIO(test, kFileIO));
142
143  // Reopen the file.
144  OpenQuotaFile(&test, kFileIO, kFileSystem);
145  fio = FindFileIO(test, kFileIO);
146  fio->SetMaxWrittenOffset(kNewFileSize);
147
148  // Close with correct max_written_offset.
149  test.ScanUntrustedMessage(
150      PpapiHostMsg_ResourceCall(
151          fio_call_params,
152          PpapiHostMsg_FileIO_Close(FileGrowth(kNewFileSize, 0))),
153      &new_msg_ptr);
154  EXPECT_FALSE(new_msg_ptr);
155  EXPECT_FALSE(FindFileIO(test, kFileIO));
156
157  // Destroy file system.
158  test.ScanUntrustedMessage(
159      PpapiHostMsg_ResourceCall(
160          fs_call_params,
161          PpapiHostMsg_ResourceDestroyed(kFileSystem)),
162      &new_msg_ptr);
163  EXPECT_FALSE(FindFileSystem(test, kFileSystem));
164}
165
166TEST_F(NaClMessageScannerTest, QuotaAuditing) {
167  NaClMessageScanner test;
168  std::vector<SerializedHandle> unused_handles;
169  ResourceMessageCallParams fio_call_params(kFileIO, 0);
170  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
171  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
172  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
173  scoped_ptr<IPC::Message> new_msg_ptr;
174
175  OpenQuotaFile(&test, kFileIO, kFileSystem);
176  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
177  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
178  EXPECT_TRUE(fs);
179  EXPECT_EQ(0, fs->reserved_quota());
180  EXPECT_TRUE(fio);
181  EXPECT_EQ(0, fio->max_written_offset());
182
183  // Without reserving quota, we should not be able to grow the file.
184  EXPECT_FALSE(fio->Grow(1));
185  EXPECT_EQ(0, fs->reserved_quota());
186  EXPECT_EQ(0, fio->max_written_offset());
187
188  // Receive reserved quota, and updated file sizes.
189  const int64_t kNewFileSize = 10;
190  FileSizeMap file_sizes;
191  file_sizes[kFileIO] = kNewFileSize;
192  test.ScanMessage(
193      PpapiPluginMsg_ResourceReply(
194          fs_reply_params,
195          PpapiPluginMsg_FileSystem_ReserveQuotaReply(
196              kQuotaReservationAmount,
197              file_sizes)),
198      &unused_handles,
199      &new_msg_ptr);
200  EXPECT_FALSE(new_msg_ptr);
201  EXPECT_EQ(kQuotaReservationAmount, fs->reserved_quota());
202  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
203
204  // We should be able to grow the file within quota.
205  EXPECT_TRUE(fio->Grow(1));
206  EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
207  EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
208
209  // We should not be able to grow the file over quota.
210  EXPECT_FALSE(fio->Grow(kQuotaReservationAmount));
211  EXPECT_EQ(kQuotaReservationAmount - 1, fs->reserved_quota());
212  EXPECT_EQ(kNewFileSize + 1, fio->max_written_offset());
213
214  // Plugin should not under-report max written offsets when reserving quota.
215  file_sizes[kFileIO] = 0;  // should be kNewFileSize + 1.
216  test.ScanUntrustedMessage(
217      PpapiHostMsg_ResourceCall(
218          fio_call_params,
219          PpapiHostMsg_FileSystem_ReserveQuota(
220              kQuotaReservationAmount,
221              FileSizeMapToFileGrowthMapForTesting(file_sizes))),
222      &new_msg_ptr);
223  EXPECT_TRUE(new_msg_ptr);
224  ResourceMessageCallParams call_params;
225  IPC::Message nested_msg;
226  int64_t amount = 0;
227  FileGrowthMap new_file_growths;
228  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
229                  *new_msg_ptr, &call_params, &nested_msg) &&
230              UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
231                  nested_msg, &amount, &new_file_growths));
232  new_msg_ptr.reset();
233  EXPECT_EQ(kQuotaReservationAmount, amount);
234  EXPECT_EQ(kNewFileSize + 1, new_file_growths[kFileIO].max_written_offset);
235}
236
237TEST_F(NaClMessageScannerTest, SetLength) {
238  NaClMessageScanner test;
239  std::vector<SerializedHandle> unused_handles;
240  ResourceMessageCallParams fio_call_params(kFileIO, 0);
241  ResourceMessageCallParams fs_call_params(kFileSystem, 0);
242  ResourceMessageReplyParams fio_reply_params(kFileIO, 0);
243  ResourceMessageReplyParams fs_reply_params(kFileSystem, 0);
244  scoped_ptr<IPC::Message> new_msg_ptr;
245
246  OpenQuotaFile(&test, kFileIO, kFileSystem);
247  NaClMessageScanner::FileSystem* fs = FindFileSystem(test, kFileSystem);
248  NaClMessageScanner::FileIO* fio = FindFileIO(test, kFileIO);
249
250  // Receive reserved quota, and updated file sizes.
251  const int64_t kNewFileSize = 10;
252  FileSizeMap file_sizes;
253  file_sizes[kFileIO] = 0;
254  test.ScanMessage(
255      PpapiPluginMsg_ResourceReply(
256          fs_reply_params,
257          PpapiPluginMsg_FileSystem_ReserveQuotaReply(
258              kQuotaReservationAmount,
259              file_sizes)),
260      &unused_handles,
261      &new_msg_ptr);
262
263  // We should be able to SetLength within quota.
264  test.ScanUntrustedMessage(
265      PpapiHostMsg_ResourceCall(
266          fio_call_params,
267          PpapiHostMsg_FileIO_SetLength(kNewFileSize)),
268      &new_msg_ptr);
269  EXPECT_FALSE(new_msg_ptr);
270  EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
271  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
272
273  // We shouldn't be able to SetLength beyond quota. The message should be
274  // rewritten to fail with length == -1.
275  test.ScanUntrustedMessage(
276      PpapiHostMsg_ResourceCall(
277          fio_call_params,
278          PpapiHostMsg_FileIO_SetLength(kQuotaReservationAmount + 1)),
279      &new_msg_ptr);
280  EXPECT_TRUE(new_msg_ptr);
281  ResourceMessageCallParams call_params;
282  IPC::Message nested_msg;
283  int64_t length = 0;
284  EXPECT_TRUE(UnpackMessage<PpapiHostMsg_ResourceCall>(
285                  *new_msg_ptr, &call_params, &nested_msg) &&
286              UnpackMessage<PpapiHostMsg_FileIO_SetLength>(
287                  nested_msg, &length));
288  new_msg_ptr.reset();
289  EXPECT_EQ(-1, length);
290  EXPECT_EQ(kQuotaReservationAmount - kNewFileSize, fs->reserved_quota());
291  EXPECT_EQ(kNewFileSize, fio->max_written_offset());
292}
293
294}  // namespace proxy
295}  // namespace ppapi
296