[M120][Tizen][Onscreen] Fix build errors for TV profile
[platform/framework/web/chromium-efl.git] / chrome / browser / process_singleton_posix_unittest.cc
1 // Copyright 2014 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 "chrome/browser/process_singleton.h"
6
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <signal.h>
10 #include <stddef.h>
11 #include <sys/types.h>
12 #include <sys/un.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15
16 #include <memory>
17 #include <string>
18 #include <vector>
19
20 #include "base/command_line.h"
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/files/scoped_temp_dir.h"
24 #include "base/functional/bind.h"
25 #include "base/location.h"
26 #include "base/memory/raw_ptr.h"
27 #include "base/posix/eintr_wrapper.h"
28 #include "base/strings/stringprintf.h"
29 #include "base/synchronization/waitable_event.h"
30 #include "base/task/single_thread_task_runner.h"
31 #include "base/test/metrics/histogram_tester.h"
32 #include "base/test/test_timeouts.h"
33 #include "base/test/thread_test_helper.h"
34 #include "base/threading/thread.h"
35 #include "base/time/time.h"
36 #include "build/build_config.h"
37 #include "chrome/common/chrome_constants.h"
38 #include "content/public/browser/browser_task_traits.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/test/browser_task_environment.h"
41 #include "net/base/network_interfaces.h"
42 #include "testing/gtest/include/gtest/gtest.h"
43
44 namespace {
45
46 class ProcessSingletonPosixTest : public testing::Test {
47  public:
48   // A ProcessSingleton exposing some protected methods for testing.
49   class TestableProcessSingleton : public ProcessSingleton {
50    public:
51     explicit TestableProcessSingleton(const base::FilePath& user_data_dir)
52         : ProcessSingleton(user_data_dir,
53                            base::BindRepeating(
54                                &TestableProcessSingleton::NotificationCallback,
55                                base::Unretained(this))) {}
56
57     std::vector<base::CommandLine::StringVector> callback_command_lines_;
58
59     using ProcessSingleton::NotifyOtherProcessWithTimeout;
60     using ProcessSingleton::NotifyOtherProcessWithTimeoutOrCreate;
61     using ProcessSingleton::OverrideCurrentPidForTesting;
62     using ProcessSingleton::OverrideKillCallbackForTesting;
63     using ProcessSingleton::StartWatching;
64
65    private:
66     bool NotificationCallback(const base::CommandLine& command_line,
67                               const base::FilePath& current_directory) {
68       callback_command_lines_.push_back(command_line.argv());
69       return true;
70     }
71   };
72
73   ProcessSingletonPosixTest()
74       : kill_callbacks_(0),
75         task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD),
76         wait_event_(base::WaitableEvent::ResetPolicy::MANUAL,
77                     base::WaitableEvent::InitialState::NOT_SIGNALED),
78         signal_event_(base::WaitableEvent::ResetPolicy::MANUAL,
79                       base::WaitableEvent::InitialState::NOT_SIGNALED),
80         process_singleton_on_thread_(nullptr) {}
81
82   void SetUp() override {
83     testing::Test::SetUp();
84
85     ProcessSingleton::DisablePromptForTesting();
86     ProcessSingleton::SkipIsChromeProcessCheckForTesting(false);
87     ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(false);
88     // Put the lock in a temporary directory.  Doesn't need to be a
89     // full profile to test this code.
90     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
91     // Use a long directory name to ensure that the socket isn't opened through
92     // the symlink.
93     user_data_path_ = temp_dir_.GetPath().Append(
94         std::string(sizeof(sockaddr_un::sun_path), 'a'));
95     ASSERT_TRUE(CreateDirectory(user_data_path_));
96
97     lock_path_ = user_data_path_.Append(chrome::kSingletonLockFilename);
98     socket_path_ = user_data_path_.Append(chrome::kSingletonSocketFilename);
99     cookie_path_ = user_data_path_.Append(chrome::kSingletonCookieFilename);
100   }
101
102   void TearDown() override {
103     scoped_refptr<base::ThreadTestHelper> io_helper(
104         new base::ThreadTestHelper(content::GetIOThreadTaskRunner({}).get()));
105     ASSERT_TRUE(io_helper->Run());
106
107     // Destruct the ProcessSingleton object before the IO thread so that its
108     // internals are destructed properly.
109     if (process_singleton_on_thread_) {
110       worker_thread_->task_runner()->PostTask(
111           FROM_HERE,
112           base::BindOnce(&ProcessSingletonPosixTest::DestructProcessSingleton,
113                          base::Unretained(this)));
114
115       scoped_refptr<base::ThreadTestHelper> helper(
116           new base::ThreadTestHelper(worker_thread_->task_runner().get()));
117       ASSERT_TRUE(helper->Run());
118     }
119
120     testing::Test::TearDown();
121   }
122
123   void CreateProcessSingletonOnThread() {
124     ASSERT_FALSE(worker_thread_.get());
125     worker_thread_ = std::make_unique<base::Thread>("BlockingThread");
126     worker_thread_->Start();
127
128     worker_thread_->task_runner()->PostTask(
129         FROM_HERE,
130         base::BindOnce(
131             &ProcessSingletonPosixTest::CreateProcessSingletonInternal,
132             base::Unretained(this)));
133
134     scoped_refptr<base::ThreadTestHelper> helper(
135         new base::ThreadTestHelper(worker_thread_->task_runner().get()));
136     ASSERT_TRUE(helper->Run());
137   }
138
139   TestableProcessSingleton* CreateProcessSingleton() {
140     return new TestableProcessSingleton(user_data_path_);
141   }
142
143   void VerifyFiles() {
144     struct stat statbuf;
145     ASSERT_EQ(0, lstat(lock_path_.value().c_str(), &statbuf));
146     ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
147     char buf[PATH_MAX];
148     ssize_t len = readlink(lock_path_.value().c_str(), buf, PATH_MAX);
149     ASSERT_GT(len, 0);
150
151     ASSERT_EQ(0, lstat(socket_path_.value().c_str(), &statbuf));
152     ASSERT_TRUE(S_ISLNK(statbuf.st_mode));
153
154     len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
155     ASSERT_GT(len, 0);
156     base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
157
158     ASSERT_EQ(0, lstat(socket_target_path.value().c_str(), &statbuf));
159     ASSERT_TRUE(S_ISSOCK(statbuf.st_mode));
160
161     len = readlink(cookie_path_.value().c_str(), buf, PATH_MAX);
162     ASSERT_GT(len, 0);
163     std::string cookie(buf, len);
164
165     base::FilePath remote_cookie_path = socket_target_path.DirName().
166         Append(chrome::kSingletonCookieFilename);
167     len = readlink(remote_cookie_path.value().c_str(), buf, PATH_MAX);
168     ASSERT_GT(len, 0);
169     EXPECT_EQ(cookie, std::string(buf, len));
170   }
171
172   ProcessSingleton::NotifyResult NotifyOtherProcess(bool override_kill) {
173     std::unique_ptr<TestableProcessSingleton> process_singleton(
174         CreateProcessSingleton());
175     base::CommandLine command_line(
176         base::CommandLine::ForCurrentProcess()->GetProgram());
177     command_line.AppendArg("about:blank");
178     if (override_kill) {
179       process_singleton->OverrideCurrentPidForTesting(
180           base::GetCurrentProcId() + 1);
181       process_singleton->OverrideKillCallbackForTesting(base::BindRepeating(
182           &ProcessSingletonPosixTest::KillCallback, base::Unretained(this)));
183     }
184
185     return process_singleton->NotifyOtherProcessWithTimeout(
186         command_line, kRetryAttempts, timeout(), true);
187   }
188
189   // A helper method to call ProcessSingleton::NotifyOtherProcessOrCreate().
190   ProcessSingleton::NotifyResult NotifyOtherProcessOrCreate(
191       const std::string& url) {
192     std::unique_ptr<TestableProcessSingleton> process_singleton(
193         CreateProcessSingleton());
194     base::CommandLine command_line(
195         base::CommandLine::ForCurrentProcess()->GetProgram());
196     command_line.AppendArg(url);
197     return process_singleton->NotifyOtherProcessWithTimeoutOrCreate(
198         command_line, kRetryAttempts, timeout());
199   }
200
201   void CheckNotified() {
202     ASSERT_TRUE(process_singleton_on_thread_);
203     ASSERT_EQ(1u, process_singleton_on_thread_->callback_command_lines_.size());
204     bool found = false;
205     for (size_t i = 0;
206          i < process_singleton_on_thread_->callback_command_lines_[0].size();
207          ++i) {
208       if (process_singleton_on_thread_->callback_command_lines_[0][i] ==
209           "about:blank") {
210         found = true;
211         break;
212       }
213     }
214     ASSERT_TRUE(found);
215     ASSERT_EQ(0, kill_callbacks_);
216   }
217
218   void BlockWorkerThread() {
219     worker_thread_->task_runner()->PostTask(
220         FROM_HERE, base::BindOnce(&ProcessSingletonPosixTest::BlockThread,
221                                   base::Unretained(this)));
222   }
223
224   void UnblockWorkerThread() {
225     wait_event_.Signal();  // Unblock the worker thread for shutdown.
226     signal_event_.Wait();  // Ensure thread unblocks before continuing.
227   }
228
229   void BlockThread() {
230     wait_event_.Wait();
231     signal_event_.Signal();
232   }
233
234   base::FilePath user_data_path_;
235   base::FilePath lock_path_;
236   base::FilePath socket_path_;
237   base::FilePath cookie_path_;
238   int kill_callbacks_;
239
240  private:
241   static const int kRetryAttempts = 2;
242
243   base::TimeDelta timeout() const {
244     return TestTimeouts::tiny_timeout() * kRetryAttempts;
245   }
246
247   void CreateProcessSingletonInternal() {
248     ASSERT_TRUE(!process_singleton_on_thread_);
249     process_singleton_on_thread_ = CreateProcessSingleton();
250     ASSERT_EQ(ProcessSingleton::PROCESS_NONE,
251               process_singleton_on_thread_->NotifyOtherProcessOrCreate());
252     process_singleton_on_thread_->StartWatching();
253   }
254
255   void DestructProcessSingleton() {
256     ASSERT_TRUE(process_singleton_on_thread_);
257     delete process_singleton_on_thread_;
258   }
259
260   void KillCallback(int pid) {
261     kill_callbacks_++;
262   }
263
264   content::BrowserTaskEnvironment task_environment_;
265   base::ScopedTempDir temp_dir_;
266   base::WaitableEvent wait_event_;
267   base::WaitableEvent signal_event_;
268
269   std::unique_ptr<base::Thread> worker_thread_;
270   raw_ptr<TestableProcessSingleton, DanglingUntriaged>
271       process_singleton_on_thread_;
272 };
273
274 }  // namespace
275
276 // Test if the socket file and symbol link created by ProcessSingletonPosix
277 // are valid.
278 // If this test flakes, use http://crbug.com/74554.
279 TEST_F(ProcessSingletonPosixTest, CheckSocketFile) {
280   CreateProcessSingletonOnThread();
281   VerifyFiles();
282 }
283
284 // TODO(james.su@gmail.com): port following tests to Windows.
285 // Test success case of NotifyOtherProcess().
286 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessSuccess) {
287   CreateProcessSingletonOnThread();
288   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED, NotifyOtherProcess(true));
289   CheckNotified();
290 }
291
292 // Test failure case of NotifyOtherProcess().
293 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessFailure) {
294   base::HistogramTester histogram_tester;
295   CreateProcessSingletonOnThread();
296
297   BlockWorkerThread();
298   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
299   ASSERT_EQ(1, kill_callbacks_);
300   UnblockWorkerThread();
301   histogram_tester.ExpectUniqueSample(
302       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
303       ProcessSingleton::SOCKET_READ_FAILED, 1u);
304 }
305
306 // Test that we don't kill ourselves by accident if a lockfile with the same pid
307 // happens to exist.
308 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessNoSuicide) {
309   base::HistogramTester histogram_tester;
310   CreateProcessSingletonOnThread();
311   // Replace lockfile with one containing our own pid.
312   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
313   std::string symlink_content = base::StringPrintf(
314       "%s%c%u",
315       net::GetHostName().c_str(),
316       '-',
317       base::GetCurrentProcId());
318   EXPECT_EQ(0, symlink(symlink_content.c_str(), lock_path_.value().c_str()));
319
320   // Remove socket so that we will not be able to notify the existing browser.
321   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
322
323   // Pretend we are browser process.
324   ProcessSingleton::SkipIsChromeProcessCheckForTesting(true);
325
326   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
327   // If we've gotten to this point without killing ourself, the test succeeded.
328   histogram_tester.ExpectUniqueSample(
329       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
330       ProcessSingleton::SAME_BROWSER_INSTANCE, 1u);
331 }
332
333 // Test that we can still notify a process on the same host even after the
334 // hostname changed.
335 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessHostChanged) {
336   CreateProcessSingletonOnThread();
337   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
338   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
339
340   EXPECT_EQ(ProcessSingleton::PROCESS_NOTIFIED, NotifyOtherProcess(false));
341   CheckNotified();
342 }
343
344 // Test that we kill hung browser when lock says process is on another host and
345 // we can't notify it over the socket.
346 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessDifferingHost) {
347   base::HistogramTester histogram_tester;
348   CreateProcessSingletonOnThread();
349
350   BlockWorkerThread();
351
352   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
353   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
354
355   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(true));
356   ASSERT_EQ(1, kill_callbacks_);
357
358   // lock_path_ should be unlinked in NotifyOtherProcess().
359   base::FilePath target_path;
360   EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
361
362   UnblockWorkerThread();
363
364   histogram_tester.ExpectUniqueSample(
365       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
366       ProcessSingleton::SOCKET_READ_FAILED, 1u);
367 }
368
369 // Test that we'll start creating ProcessSingleton when we have old lock file
370 // that says process is on another host and there is browser with the same pid
371 // but with another user data dir. Also suppose that user opted to unlock
372 // profile.
373 TEST_F(ProcessSingletonPosixTest,
374        NotifyOtherProcessDifferingHost_UnlockedProfileBeforeKill) {
375   base::HistogramTester histogram_tester;
376   CreateProcessSingletonOnThread();
377
378   BlockWorkerThread();
379
380   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
381   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
382
383   // Remove socket so that we will not be able to notify the existing browser.
384   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
385
386   // Unlock profile that was locked by process on another host.
387   ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
388   // Treat process with pid 1234 as browser with different user data dir.
389   ProcessSingleton::SkipIsChromeProcessCheckForTesting(true);
390
391   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcess(false));
392
393   // lock_path_ should be unlinked in NotifyOtherProcess().
394   base::FilePath target_path;
395   EXPECT_FALSE(base::ReadSymbolicLink(lock_path_, &target_path));
396
397   UnblockWorkerThread();
398
399   histogram_tester.ExpectUniqueSample(
400       "Chrome.ProcessSingleton.RemoteHungProcessTerminateReason",
401       ProcessSingleton::NOTIFY_ATTEMPTS_EXCEEDED, 1u);
402   histogram_tester.ExpectUniqueSample(
403       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
404       ProcessSingleton::PROFILE_UNLOCKED_BEFORE_KILL, 1u);
405 }
406
407 // Test that we unlock profile when lock says process is on another host and we
408 // can't notify it over the socket.
409 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_DifferingHost) {
410   base::HistogramTester histogram_tester;
411   CreateProcessSingletonOnThread();
412
413   BlockWorkerThread();
414
415   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
416   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
417
418   // Remove socket so that we will not be able to notify the existing browser.
419   EXPECT_EQ(0, unlink(socket_path_.value().c_str()));
420   // Unlock profile that was locked by process on another host.
421   ProcessSingleton::SetUserOptedUnlockInUseProfileForTesting(true);
422
423   std::string url("about:blank");
424   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcessOrCreate(url));
425
426   ASSERT_EQ(0, unlink(lock_path_.value().c_str()));
427
428   UnblockWorkerThread();
429
430   histogram_tester.ExpectUniqueSample(
431       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
432       ProcessSingleton::PROFILE_UNLOCKED, 1u);
433 }
434
435 // Test that Create fails when another browser is using the profile directory.
436 TEST_F(ProcessSingletonPosixTest, CreateFailsWithExistingBrowser) {
437   CreateProcessSingletonOnThread();
438
439   std::unique_ptr<TestableProcessSingleton> process_singleton(
440       CreateProcessSingleton());
441   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
442   EXPECT_FALSE(process_singleton->Create());
443 }
444
445 // Test that Create fails when another browser is using the profile directory
446 // but with the old socket location.
447 TEST_F(ProcessSingletonPosixTest, CreateChecksCompatibilitySocket) {
448   CreateProcessSingletonOnThread();
449   std::unique_ptr<TestableProcessSingleton> process_singleton(
450       CreateProcessSingleton());
451   process_singleton->OverrideCurrentPidForTesting(base::GetCurrentProcId() + 1);
452
453   // Do some surgery so as to look like the old configuration.
454   char buf[PATH_MAX];
455   ssize_t len = readlink(socket_path_.value().c_str(), buf, sizeof(buf));
456   ASSERT_GT(len, 0);
457   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
458   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
459   ASSERT_EQ(0, rename(socket_target_path.value().c_str(),
460                       socket_path_.value().c_str()));
461   ASSERT_EQ(0, unlink(cookie_path_.value().c_str()));
462
463   EXPECT_FALSE(process_singleton->Create());
464 }
465
466 // Test that we fail when lock says process is on another host and we can't
467 // notify it over the socket before of a bad cookie.
468 TEST_F(ProcessSingletonPosixTest, NotifyOtherProcessOrCreate_BadCookie) {
469   CreateProcessSingletonOnThread();
470   // Change the cookie.
471   EXPECT_EQ(0, unlink(cookie_path_.value().c_str()));
472   EXPECT_EQ(0, symlink("INCORRECTCOOKIE", cookie_path_.value().c_str()));
473
474   // Also change the hostname, so the remote does not retry.
475   EXPECT_EQ(0, unlink(lock_path_.value().c_str()));
476   EXPECT_EQ(0, symlink("FAKEFOOHOST-1234", lock_path_.value().c_str()));
477
478   std::string url("about:blank");
479   EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, NotifyOtherProcessOrCreate(url));
480 }
481
482 TEST_F(ProcessSingletonPosixTest, IgnoreSocketSymlinkWithTooLongTarget) {
483   base::HistogramTester histogram_tester;
484   CreateProcessSingletonOnThread();
485   // Change the symlink to one with a too-long target.
486   char buf[PATH_MAX];
487   ssize_t len = readlink(socket_path_.value().c_str(), buf, PATH_MAX);
488   ASSERT_GT(len, 0);
489   base::FilePath socket_target_path = base::FilePath(std::string(buf, len));
490   base::FilePath long_socket_target_path = socket_target_path.DirName().Append(
491       std::string(sizeof(sockaddr_un::sun_path), 'b'));
492   ASSERT_EQ(0, unlink(socket_path_.value().c_str()));
493   ASSERT_EQ(0, symlink(long_socket_target_path.value().c_str(),
494                        socket_path_.value().c_str()));
495
496   // A new ProcessSingleton should ignore the invalid socket path target.
497   std::string url("about:blank");
498   EXPECT_EQ(ProcessSingleton::PROCESS_NONE, NotifyOtherProcessOrCreate(url));
499
500   // Lock file contains PID of unit_tests process. It is non browser process so
501   // we treat lock file as orphaned.
502   histogram_tester.ExpectUniqueSample(
503       "Chrome.ProcessSingleton.RemoteProcessInteractionResult",
504       ProcessSingleton::ORPHANED_LOCK_FILE, 1u);
505 }
506
507 #if BUILDFLAG(IS_MAC)
508 // Test that if there is an existing lock file, and we could not flock()
509 // it, then exit.
510 TEST_F(ProcessSingletonPosixTest, CreateRespectsOldMacLock) {
511   std::unique_ptr<TestableProcessSingleton> process_singleton(
512       CreateProcessSingleton());
513   base::ScopedFD lock_fd(HANDLE_EINTR(
514       open(lock_path_.value().c_str(), O_RDWR | O_CREAT | O_EXLOCK, 0644)));
515   ASSERT_TRUE(lock_fd.is_valid());
516   EXPECT_FALSE(process_singleton->Create());
517   base::File::Info info;
518   EXPECT_TRUE(base::GetFileInfo(lock_path_, &info));
519   EXPECT_FALSE(info.is_directory);
520   EXPECT_FALSE(info.is_symbolic_link);
521 }
522
523 // Test that if there is an existing lock file, and it's not locked, we replace
524 // it.
525 TEST_F(ProcessSingletonPosixTest, CreateReplacesOldMacLock) {
526   std::unique_ptr<TestableProcessSingleton> process_singleton(
527       CreateProcessSingleton());
528   EXPECT_TRUE(base::WriteFile(lock_path_, base::StringPiece()));
529   EXPECT_TRUE(process_singleton->Create());
530   VerifyFiles();
531 }
532 #endif  // BUILDFLAG(IS_MAC)