• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
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 "components/nacl/browser/pnacl_host.h"
6 
7 #include <stddef.h>
8 #include <stdio.h>
9 #include <string.h>
10 
11 #include <string>
12 
13 #include "base/compiler_specific.h"
14 #include "base/files/scoped_temp_dir.h"
15 #include "base/functional/bind.h"
16 #include "base/memory/raw_ptr.h"
17 #include "base/run_loop.h"
18 #include "build/build_config.h"
19 #include "components/nacl/browser/pnacl_translation_cache.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "content/public/test/test_utils.h"
23 #include "net/base/test_completion_callback.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 namespace pnacl {
28 namespace {
29 
30 // Size of a buffer used for writing and reading from a file.
31 const size_t kBufferSize = 16u;
32 
33 }  // namespace
34 
35 class PnaclHostTest : public testing::Test {
36  protected:
PnaclHostTest()37   PnaclHostTest()
38       : host_(nullptr),
39         temp_callback_count_(0),
40         write_callback_count_(0),
41         task_environment_(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
SetUp()42   void SetUp() override {
43     host_ = PnaclHost::GetInstance();
44     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
45     host_->InitForTest(temp_dir_.GetPath(), true);
46     base::RunLoop().RunUntilIdle();
47     EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_);
48   }
TearDown()49   void TearDown() override {
50     EXPECT_EQ(0U, host_->pending_translations());
51     // Give the host a chance to de-init the backend, and then delete it.
52     host_->RendererClosing(0);
53     content::RunAllTasksUntilIdle();
54     disk_cache::FlushCacheThreadForTesting();
55     EXPECT_EQ(PnaclHost::CacheUninitialized, host_->cache_state_);
56   }
GetCacheSize()57   int GetCacheSize() { return host_->disk_cache_->Size(); }
CacheIsInitialized()58   int CacheIsInitialized() {
59     return host_->cache_state_ == PnaclHost::CacheReady;
60   }
ReInitBackend()61   void ReInitBackend() {
62     host_->InitForTest(temp_dir_.GetPath(), true);
63     base::RunLoop().RunUntilIdle();
64     EXPECT_EQ(PnaclHost::CacheReady, host_->cache_state_);
65   }
66 
67  public:  // Required for derived classes to bind this method
68           // Callbacks used by tests which call GetNexeFd.
69   // CallbackExpectMiss checks that the fd is valid and a miss is reported,
70   // and also writes some data into the file, which is read back by
71   // CallbackExpectHit
CallbackExpectMiss(const base::File & file,bool is_hit)72   void CallbackExpectMiss(const base::File& file, bool is_hit) {
73     EXPECT_FALSE(is_hit);
74     ASSERT_TRUE(file.IsValid());
75     base::File::Info info;
76     base::File* mutable_file = const_cast<base::File*>(&file);
77     EXPECT_TRUE(mutable_file->GetInfo(&info));
78     EXPECT_FALSE(info.is_directory);
79     EXPECT_EQ(0LL, info.size);
80     char str[kBufferSize];
81     memset(str, 0x0, kBufferSize);
82     snprintf(str, kBufferSize, "testdata%d", ++write_callback_count_);
83     EXPECT_EQ(kBufferSize, static_cast<size_t>(UNSAFE_TODO(
84                                mutable_file->Write(0, str, kBufferSize))));
85     temp_callback_count_++;
86   }
CallbackExpectHit(const base::File & file,bool is_hit)87   void CallbackExpectHit(const base::File& file, bool is_hit) {
88     EXPECT_TRUE(is_hit);
89     ASSERT_TRUE(file.IsValid());
90     base::File::Info info;
91     base::File* mutable_file = const_cast<base::File*>(&file);
92     EXPECT_TRUE(mutable_file->GetInfo(&info));
93     EXPECT_FALSE(info.is_directory);
94     EXPECT_EQ(kBufferSize, static_cast<size_t>(info.size));
95     char data[kBufferSize];
96     memset(data, 0x0, kBufferSize);
97     char str[kBufferSize];
98     memset(str, 0x0, kBufferSize);
99     snprintf(str, kBufferSize, "testdata%d", write_callback_count_);
100     EXPECT_EQ(kBufferSize, static_cast<size_t>(UNSAFE_TODO(
101                                mutable_file->Read(0, data, kBufferSize))));
102     EXPECT_STREQ(str, data);
103     temp_callback_count_++;
104   }
105 
106  protected:
107   raw_ptr<PnaclHost> host_;
108   int temp_callback_count_;
109   int write_callback_count_;
110   content::BrowserTaskEnvironment task_environment_;
111   base::ScopedTempDir temp_dir_;
112 };
113 
GetTestCacheInfo()114 static nacl::PnaclCacheInfo GetTestCacheInfo() {
115   nacl::PnaclCacheInfo info;
116   info.pexe_url = GURL("http://www.google.com");
117   info.abi_version = 0;
118   info.opt_level = 0;
119   info.has_no_store_header = false;
120   info.use_subzero = false;
121   return info;
122 }
123 
124 #define GET_NEXE_FD(renderer, instance, incognito, info, expect_hit)         \
125   do {                                                                       \
126     SCOPED_TRACE("");                                                        \
127     host_->GetNexeFd(                                                        \
128         renderer, instance, incognito, info,                                 \
129         base::BindRepeating(expect_hit ? &PnaclHostTest::CallbackExpectHit   \
130                                        : &PnaclHostTest::CallbackExpectMiss, \
131                             base::Unretained(this)));                        \
132   } while (0)
133 
TEST_F(PnaclHostTest,BasicMiss)134 TEST_F(PnaclHostTest, BasicMiss) {
135   nacl::PnaclCacheInfo info = GetTestCacheInfo();
136   // Test cold miss.
137   GET_NEXE_FD(0, 0, false, info, false);
138   EXPECT_EQ(1U, host_->pending_translations());
139   content::RunAllTasksUntilIdle();
140   EXPECT_EQ(1U, host_->pending_translations());
141   EXPECT_EQ(1, temp_callback_count_);
142   host_->TranslationFinished(0, 0, true);
143   content::RunAllTasksUntilIdle();
144   EXPECT_EQ(0U, host_->pending_translations());
145   // Test that a different cache info field also misses.
146   info.etag = std::string("something else");
147   GET_NEXE_FD(0, 0, false, info, false);
148   content::RunAllTasksUntilIdle();
149   EXPECT_EQ(2, temp_callback_count_);
150   EXPECT_EQ(1U, host_->pending_translations());
151   host_->RendererClosing(0);
152   content::RunAllTasksUntilIdle();
153   // Check that the cache has de-initialized after the last renderer goes away.
154   EXPECT_FALSE(CacheIsInitialized());
155 }
156 
TEST_F(PnaclHostTest,BadArguments)157 TEST_F(PnaclHostTest, BadArguments) {
158   nacl::PnaclCacheInfo info = GetTestCacheInfo();
159   GET_NEXE_FD(0, 0, false, info, false);
160   EXPECT_EQ(1U, host_->pending_translations());
161   host_->TranslationFinished(0, 1, true);  // nonexistent translation
162   EXPECT_EQ(1U, host_->pending_translations());
163   host_->RendererClosing(1);  // nonexistent renderer
164   EXPECT_EQ(1U, host_->pending_translations());
165   content::RunAllTasksUntilIdle();
166   EXPECT_EQ(1, temp_callback_count_);
167   host_->RendererClosing(0);  // close without finishing
168 }
169 
TEST_F(PnaclHostTest,BasicHit)170 TEST_F(PnaclHostTest, BasicHit) {
171   nacl::PnaclCacheInfo info = GetTestCacheInfo();
172   GET_NEXE_FD(0, 0, false, info, false);
173   content::RunAllTasksUntilIdle();
174   EXPECT_EQ(1, temp_callback_count_);
175   host_->TranslationFinished(0, 0, true);
176   content::RunAllTasksUntilIdle();
177   GET_NEXE_FD(0, 1, false, info, true);
178   content::RunAllTasksUntilIdle();
179   EXPECT_EQ(2, temp_callback_count_);
180   EXPECT_EQ(0U, host_->pending_translations());
181 }
182 
TEST_F(PnaclHostTest,TranslationErrors)183 TEST_F(PnaclHostTest, TranslationErrors) {
184   nacl::PnaclCacheInfo info = GetTestCacheInfo();
185   GET_NEXE_FD(0, 0, false, info, false);
186   // Early abort, before temp file request returns
187   host_->TranslationFinished(0, 0, false);
188   content::RunAllTasksUntilIdle();
189   EXPECT_EQ(0U, host_->pending_translations());
190   EXPECT_EQ(0, temp_callback_count_);
191   // The backend will have been freed when the query comes back and there
192   // are no pending translations.
193   EXPECT_FALSE(CacheIsInitialized());
194   ReInitBackend();
195   // Check that another request for the same info misses successfully.
196   GET_NEXE_FD(0, 0, false, info, false);
197   content::RunAllTasksUntilIdle();
198   host_->TranslationFinished(0, 0, true);
199   content::RunAllTasksUntilIdle();
200   EXPECT_EQ(1, temp_callback_count_);
201   EXPECT_EQ(0U, host_->pending_translations());
202 
203   // Now try sending the error after the temp file request returns
204   info.abi_version = 222;
205   GET_NEXE_FD(0, 0, false, info, false);
206   content::RunAllTasksUntilIdle();
207   EXPECT_EQ(2, temp_callback_count_);
208   host_->TranslationFinished(0, 0, false);
209   content::RunAllTasksUntilIdle();
210   EXPECT_EQ(0U, host_->pending_translations());
211   // Check another successful miss
212   GET_NEXE_FD(0, 0, false, info, false);
213   content::RunAllTasksUntilIdle();
214   EXPECT_EQ(3, temp_callback_count_);
215   host_->TranslationFinished(0, 0, false);
216   EXPECT_EQ(0U, host_->pending_translations());
217 }
218 
TEST_F(PnaclHostTest,OverlappedMissesAfterTempReturn)219 TEST_F(PnaclHostTest, OverlappedMissesAfterTempReturn) {
220   nacl::PnaclCacheInfo info = GetTestCacheInfo();
221   GET_NEXE_FD(0, 0, false, info, false);
222   content::RunAllTasksUntilIdle();
223   EXPECT_EQ(1, temp_callback_count_);
224   EXPECT_EQ(1U, host_->pending_translations());
225   // Test that a second request for the same nexe while the first one is still
226   // outstanding eventually hits.
227   GET_NEXE_FD(0, 1, false, info, true);
228   content::RunAllTasksUntilIdle();
229   EXPECT_EQ(2U, host_->pending_translations());
230   // The temp file should not be returned to the second request until after the
231   // first is finished translating.
232   EXPECT_EQ(1, temp_callback_count_);
233   host_->TranslationFinished(0, 0, true);
234   content::RunAllTasksUntilIdle();
235   EXPECT_EQ(2, temp_callback_count_);
236   EXPECT_EQ(0U, host_->pending_translations());
237 }
238 
TEST_F(PnaclHostTest,OverlappedMissesBeforeTempReturn)239 TEST_F(PnaclHostTest, OverlappedMissesBeforeTempReturn) {
240   nacl::PnaclCacheInfo info = GetTestCacheInfo();
241   GET_NEXE_FD(0, 0, false, info, false);
242   // Send the 2nd fd request before the first one returns a temp file.
243   GET_NEXE_FD(0, 1, false, info, true);
244   content::RunAllTasksUntilIdle();
245   EXPECT_EQ(1, temp_callback_count_);
246   EXPECT_EQ(2U, host_->pending_translations());
247   content::RunAllTasksUntilIdle();
248   EXPECT_EQ(2U, host_->pending_translations());
249   EXPECT_EQ(1, temp_callback_count_);
250   host_->TranslationFinished(0, 0, true);
251   content::RunAllTasksUntilIdle();
252   EXPECT_EQ(2, temp_callback_count_);
253   EXPECT_EQ(0U, host_->pending_translations());
254 }
255 
TEST_F(PnaclHostTest,OverlappedHitsBeforeTempReturn)256 TEST_F(PnaclHostTest, OverlappedHitsBeforeTempReturn) {
257   nacl::PnaclCacheInfo info = GetTestCacheInfo();
258   // Store one in the cache and complete it.
259   GET_NEXE_FD(0, 0, false, info, false);
260   content::RunAllTasksUntilIdle();
261   EXPECT_EQ(1, temp_callback_count_);
262   host_->TranslationFinished(0, 0, true);
263   content::RunAllTasksUntilIdle();
264   EXPECT_EQ(0U, host_->pending_translations());
265   GET_NEXE_FD(0, 0, false, info, true);
266   // Request the second before the first temp file returns.
267   GET_NEXE_FD(0, 1, false, info, true);
268   content::RunAllTasksUntilIdle();
269   EXPECT_EQ(3, temp_callback_count_);
270   EXPECT_EQ(0U, host_->pending_translations());
271 }
272 
TEST_F(PnaclHostTest,OverlappedHitsAfterTempReturn)273 TEST_F(PnaclHostTest, OverlappedHitsAfterTempReturn) {
274   nacl::PnaclCacheInfo info = GetTestCacheInfo();
275   // Store one in the cache and complete it.
276   GET_NEXE_FD(0, 0, false, info, false);
277   content::RunAllTasksUntilIdle();
278   EXPECT_EQ(1, temp_callback_count_);
279   host_->TranslationFinished(0, 0, true);
280   content::RunAllTasksUntilIdle();
281   EXPECT_EQ(0U, host_->pending_translations());
282   GET_NEXE_FD(0, 0, false, info, true);
283   content::RunAllTasksUntilIdle();
284   GET_NEXE_FD(0, 1, false, info, true);
285   content::RunAllTasksUntilIdle();
286   EXPECT_EQ(3, temp_callback_count_);
287   EXPECT_EQ(0U, host_->pending_translations());
288 }
289 
TEST_F(PnaclHostTest,OverlappedMissesRendererClosing)290 TEST_F(PnaclHostTest, OverlappedMissesRendererClosing) {
291   nacl::PnaclCacheInfo info = GetTestCacheInfo();
292   GET_NEXE_FD(0, 0, false, info, false);
293   // Send the 2nd fd request from a different renderer.
294   // Test that it eventually gets an fd after the first renderer closes.
295   GET_NEXE_FD(1, 1, false, info, false);
296   content::RunAllTasksUntilIdle();
297   EXPECT_EQ(1, temp_callback_count_);
298   EXPECT_EQ(2U, host_->pending_translations());
299   content::RunAllTasksUntilIdle();
300   EXPECT_EQ(2U, host_->pending_translations());
301   EXPECT_EQ(1, temp_callback_count_);
302   host_->RendererClosing(0);
303   content::RunAllTasksUntilIdle();
304   EXPECT_EQ(2, temp_callback_count_);
305   EXPECT_EQ(1U, host_->pending_translations());
306   host_->RendererClosing(1);
307 }
308 
TEST_F(PnaclHostTest,Incognito)309 TEST_F(PnaclHostTest, Incognito) {
310   nacl::PnaclCacheInfo info = GetTestCacheInfo();
311   GET_NEXE_FD(0, 0, true, info, false);
312   content::RunAllTasksUntilIdle();
313   EXPECT_EQ(1, temp_callback_count_);
314   host_->TranslationFinished(0, 0, true);
315   content::RunAllTasksUntilIdle();
316   // Check that an incognito translation is not stored in the cache
317   GET_NEXE_FD(0, 0, false, info, false);
318   content::RunAllTasksUntilIdle();
319   EXPECT_EQ(2, temp_callback_count_);
320   host_->TranslationFinished(0, 0, true);
321   content::RunAllTasksUntilIdle();
322   // Check that an incognito translation can hit from a normal one.
323   GET_NEXE_FD(0, 0, true, info, true);
324   content::RunAllTasksUntilIdle();
325   EXPECT_EQ(3, temp_callback_count_);
326 }
327 
TEST_F(PnaclHostTest,IncognitoOverlappedMiss)328 TEST_F(PnaclHostTest, IncognitoOverlappedMiss) {
329   nacl::PnaclCacheInfo info = GetTestCacheInfo();
330   GET_NEXE_FD(0, 0, true, info, false);
331   GET_NEXE_FD(0, 1, false, info, false);
332   content::RunAllTasksUntilIdle();
333   // Check that both translations have returned misses, (i.e. that the
334   // second one has not blocked on the incognito one)
335   EXPECT_EQ(2, temp_callback_count_);
336   host_->TranslationFinished(0, 0, true);
337   host_->TranslationFinished(0, 1, true);
338   content::RunAllTasksUntilIdle();
339   EXPECT_EQ(0U, host_->pending_translations());
340 
341   // Same test, but issue the 2nd request after the first has returned a miss.
342   info.abi_version = 222;
343   GET_NEXE_FD(0, 0, true, info, false);
344   content::RunAllTasksUntilIdle();
345   EXPECT_EQ(3, temp_callback_count_);
346   GET_NEXE_FD(0, 1, false, info, false);
347   content::RunAllTasksUntilIdle();
348   EXPECT_EQ(4, temp_callback_count_);
349   host_->RendererClosing(0);
350 }
351 
TEST_F(PnaclHostTest,IncognitoSecondOverlappedMiss)352 TEST_F(PnaclHostTest, IncognitoSecondOverlappedMiss) {
353   // If the non-incognito request comes first, it should
354   // behave exactly like OverlappedMissBeforeTempReturn
355   nacl::PnaclCacheInfo info = GetTestCacheInfo();
356   GET_NEXE_FD(0, 0, false, info, false);
357   // Send the 2nd fd request before the first one returns a temp file.
358   GET_NEXE_FD(0, 1, true, info, true);
359   content::RunAllTasksUntilIdle();
360   EXPECT_EQ(1, temp_callback_count_);
361   EXPECT_EQ(2U, host_->pending_translations());
362   content::RunAllTasksUntilIdle();
363   EXPECT_EQ(2U, host_->pending_translations());
364   EXPECT_EQ(1, temp_callback_count_);
365   host_->TranslationFinished(0, 0, true);
366   content::RunAllTasksUntilIdle();
367   EXPECT_EQ(2, temp_callback_count_);
368   EXPECT_EQ(0U, host_->pending_translations());
369 }
370 
371 // Test that pexes with the no-store header do not get cached.
TEST_F(PnaclHostTest,CacheControlNoStore)372 TEST_F(PnaclHostTest, CacheControlNoStore) {
373   nacl::PnaclCacheInfo info = GetTestCacheInfo();
374   info.has_no_store_header = true;
375   GET_NEXE_FD(0, 0, false, info, false);
376   content::RunAllTasksUntilIdle();
377   EXPECT_EQ(1, temp_callback_count_);
378   host_->TranslationFinished(0, 0, true);
379   content::RunAllTasksUntilIdle();
380   EXPECT_EQ(0U, host_->pending_translations());
381   EXPECT_EQ(0, GetCacheSize());
382 }
383 
384 // Test that no-store pexes do not wait, but do duplicate translations
TEST_F(PnaclHostTest,NoStoreOverlappedMiss)385 TEST_F(PnaclHostTest, NoStoreOverlappedMiss) {
386   nacl::PnaclCacheInfo info = GetTestCacheInfo();
387   info.has_no_store_header = true;
388   GET_NEXE_FD(0, 0, false, info, false);
389   GET_NEXE_FD(0, 1, false, info, false);
390   content::RunAllTasksUntilIdle();
391   // Check that both translations have returned misses, (i.e. that the
392   // second one has not blocked on the first one)
393   EXPECT_EQ(2, temp_callback_count_);
394   host_->TranslationFinished(0, 0, true);
395   host_->TranslationFinished(0, 1, true);
396   content::RunAllTasksUntilIdle();
397   EXPECT_EQ(0U, host_->pending_translations());
398 
399   // Same test, but issue the 2nd request after the first has returned a miss.
400   info.abi_version = 222;
401   GET_NEXE_FD(0, 0, false, info, false);
402   content::RunAllTasksUntilIdle();
403   EXPECT_EQ(3, temp_callback_count_);
404   GET_NEXE_FD(0, 1, false, info, false);
405   content::RunAllTasksUntilIdle();
406   EXPECT_EQ(4, temp_callback_count_);
407   host_->RendererClosing(0);
408 }
409 
TEST_F(PnaclHostTest,ClearTranslationCache)410 TEST_F(PnaclHostTest, ClearTranslationCache) {
411   nacl::PnaclCacheInfo info = GetTestCacheInfo();
412   // Add 2 entries in the cache
413   GET_NEXE_FD(0, 0, false, info, false);
414   info.abi_version = 222;
415   GET_NEXE_FD(0, 1, false, info, false);
416   content::RunAllTasksUntilIdle();
417   EXPECT_EQ(2, temp_callback_count_);
418   host_->TranslationFinished(0, 0, true);
419   host_->TranslationFinished(0, 1, true);
420   content::RunAllTasksUntilIdle();
421   EXPECT_EQ(0U, host_->pending_translations());
422   EXPECT_EQ(2, GetCacheSize());
423   net::TestCompletionCallback cb;
424   // Since we are using a memory backend, the clear should happen immediately.
425   host_->ClearTranslationCacheEntriesBetween(base::Time(), base::Time(),
426                                              base::BindOnce(cb.callback(), 0));
427   // Check that the translation cache has been cleared before flushing the
428   // queues, because the backend will be freed once it is.
429   EXPECT_EQ(0, GetCacheSize());
430   EXPECT_EQ(0, cb.GetResult(net::ERR_IO_PENDING));
431   // Call posted PnaclHost::CopyFileToBuffer() tasks.
432   base::RunLoop().RunUntilIdle();
433   // Now check that the backend has been freed.
434   EXPECT_FALSE(CacheIsInitialized());
435 }
436 
437 // A version of PnaclHostTest that initializes cache on disk.
438 class PnaclHostTestDisk : public PnaclHostTest {
439  protected:
SetUp()440   void SetUp() override {
441     host_ = PnaclHost::GetInstance();
442     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
443     host_->InitForTest(temp_dir_.GetPath(), false);
444     EXPECT_EQ(PnaclHost::CacheInitializing, host_->cache_state_);
445   }
DeInit()446   void DeInit() {
447     host_->DeInitIfSafe();
448   }
449 };
TEST_F(PnaclHostTestDisk,DeInitWhileInitializing)450 TEST_F(PnaclHostTestDisk, DeInitWhileInitializing) {
451   // Since there's no easy way to pump message queues one message at a time, we
452   // have to simulate what would happen if 1 DeInitIfsafe task gets queued, then
453   // a GetNexeFd gets queued, and then another DeInitIfSafe gets queued before
454   // the first one runs. We can just shortcut and call DeInitIfSafe while the
455   // cache is still initializing.
456   DeInit();
457 
458   // Now let it finish initializing. (Other tests don't need this since they
459   // use in-memory storage).
460   disk_cache::FlushCacheThreadForTesting();
461   base::RunLoop().RunUntilIdle();
462   EXPECT_TRUE(CacheIsInitialized());
463 }
464 
465 }  // namespace pnacl
466