• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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 "base/allocator/partition_allocator/starscan/write_protector.h"
6 
7 #include <mutex>
8 #include <thread>
9 
10 #include "base/allocator/partition_allocator/address_pool_manager.h"
11 #include "base/allocator/partition_allocator/partition_address_space.h"
12 #include "base/allocator/partition_allocator/partition_alloc_base/logging.h"
13 #include "base/allocator/partition_allocator/partition_alloc_base/posix/eintr_wrapper.h"
14 #include "base/allocator/partition_allocator/partition_alloc_base/threading/platform_thread.h"
15 #include "base/allocator/partition_allocator/partition_alloc_check.h"
16 #include "build/build_config.h"
17 
18 #if PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
19 #include <fcntl.h>
20 #include <linux/userfaultfd.h>
21 #include <poll.h>
22 #include <sys/ioctl.h>
23 #include <sys/stat.h>
24 #include <sys/syscall.h>
25 #include <sys/types.h>
26 #endif  // PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
27 
28 namespace partition_alloc::internal {
29 
SupportedClearType() const30 PCScan::ClearType NoWriteProtector::SupportedClearType() const {
31   return PCScan::ClearType::kLazy;
32 }
33 
34 #if PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
35 
36 namespace {
UserFaultFDThread(int uffd)37 void UserFaultFDThread(int uffd) {
38   PA_DCHECK(-1 != uffd);
39 
40   static constexpr char kThreadName[] = "PCScanPFHandler";
41   internal::base::PlatformThread::SetName(kThreadName);
42 
43   while (true) {
44     // Pool on the uffd descriptor for page fault events.
45     pollfd pollfd{.fd = uffd, .events = POLLIN};
46     const int nready = PA_HANDLE_EINTR(poll(&pollfd, 1, -1));
47     PA_CHECK(-1 != nready);
48 
49     // Get page fault info.
50     uffd_msg msg;
51     const int nread = PA_HANDLE_EINTR(read(uffd, &msg, sizeof(msg)));
52     PA_CHECK(0 != nread);
53 
54     // We only expect page faults.
55     PA_DCHECK(UFFD_EVENT_PAGEFAULT == msg.event);
56     // We have subscribed only to wp-fault events.
57     PA_DCHECK(UFFD_PAGEFAULT_FLAG_WP & msg.arg.pagefault.flags);
58 
59     // Enter the safepoint. Concurrent faulted writes will wait until safepoint
60     // finishes.
61     PCScan::JoinScanIfNeeded();
62   }
63 }
64 }  // namespace
65 
UserFaultFDWriteProtector()66 UserFaultFDWriteProtector::UserFaultFDWriteProtector()
67     : uffd_(syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) {
68   if (uffd_ == -1) {
69     PA_LOG(WARNING) << "userfaultfd is not supported by the current kernel";
70     return;
71   }
72 
73   PA_PCHECK(-1 != uffd_);
74 
75   uffdio_api uffdio_api;
76   uffdio_api.api = UFFD_API;
77   uffdio_api.features = 0;
78   PA_CHECK(-1 != ioctl(uffd_, UFFDIO_API, &uffdio_api));
79   PA_CHECK(UFFD_API == uffdio_api.api);
80 
81   // Register the regular pool to listen uffd events.
82   struct uffdio_register uffdio_register;
83   uffdio_register.range.start = PartitionAddressSpace::RegularPoolBase();
84   uffdio_register.range.len = kPoolMaxSize;
85   uffdio_register.mode = UFFDIO_REGISTER_MODE_WP;
86   PA_CHECK(-1 != ioctl(uffd_, UFFDIO_REGISTER, &uffdio_register));
87 
88   // Start uffd thread.
89   std::thread(UserFaultFDThread, uffd_).detach();
90 }
91 
92 namespace {
93 enum class UserFaultFDWPMode {
94   kProtect,
95   kUnprotect,
96 };
97 
UserFaultFDWPSet(int uffd,uintptr_t begin,size_t length,UserFaultFDWPMode mode)98 void UserFaultFDWPSet(int uffd,
99                       uintptr_t begin,
100                       size_t length,
101                       UserFaultFDWPMode mode) {
102   PA_DCHECK(0 == (begin % SystemPageSize()));
103   PA_DCHECK(0 == (length % SystemPageSize()));
104 
105   uffdio_writeprotect wp;
106   wp.range.start = begin;
107   wp.range.len = length;
108   wp.mode =
109       (mode == UserFaultFDWPMode::kProtect) ? UFFDIO_WRITEPROTECT_MODE_WP : 0;
110   PA_PCHECK(-1 != ioctl(uffd, UFFDIO_WRITEPROTECT, &wp));
111 }
112 }  // namespace
113 
ProtectPages(uintptr_t begin,size_t length)114 void UserFaultFDWriteProtector::ProtectPages(uintptr_t begin, size_t length) {
115   if (IsSupported())
116     UserFaultFDWPSet(uffd_, begin, length, UserFaultFDWPMode::kProtect);
117 }
118 
UnprotectPages(uintptr_t begin,size_t length)119 void UserFaultFDWriteProtector::UnprotectPages(uintptr_t begin, size_t length) {
120   if (IsSupported())
121     UserFaultFDWPSet(uffd_, begin, length, UserFaultFDWPMode::kUnprotect);
122 }
123 
SupportedClearType() const124 PCScan::ClearType UserFaultFDWriteProtector::SupportedClearType() const {
125   return IsSupported() ? PCScan::ClearType::kEager : PCScan::ClearType::kLazy;
126 }
127 
IsSupported() const128 bool UserFaultFDWriteProtector::IsSupported() const {
129   return uffd_ != -1;
130 }
131 
132 #endif  // PA_CONFIG(STARSCAN_UFFD_WRITE_PROTECTOR_SUPPORTED)
133 
134 }  // namespace partition_alloc::internal
135