Flutter Engine
ptrace_check.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter 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 // It is __imperative__ that the functions in this file are __not__ included in
6 // release or profile builds.
7 //
8 // They call into the "private" ptrace() API to ensure that the current process
9 // is being ptrace()-d. Only debug builds rely on ptrace(), and the ptrace() API
10 // is not allowed for use in the App Store, so we must exclude it from profile-
11 // and release-builds.
12 //
13 // When an app is launched from a host workstation (e.g. via Xcode or
14 // "ios-deploy"), the process is already ptrace()-d by debugserver. However,
15 // when an app is launched from the home screen, it is not, so for debug builds
16 // we initialize the ptrace() relationship via PT_TRACE_ME if necessary.
17 //
18 // Please see the following documents for more details:
19 // - go/decommissioning-dbc
20 // - go/decommissioning-dbc-engine
21 // - go/decommissioning-dbc-tools
22 
23 #include "flutter/runtime/ptrace_check.h"
24 
25 #if TRACING_CHECKS_NECESSARY
26 
27 #include <sys/sysctl.h>
28 #include <sys/types.h>
29 
30 #include <mutex>
31 
32 #include "flutter/fml/build_config.h"
33 
34 // Being extra careful and adding additional landmines that will prevent
35 // compilation of this TU in an incorrect runtime mode.
36 static_assert(OS_IOS, "This translation unit is iOS specific.");
37 static_assert(FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG,
38  "This translation unit must only be compiled in the debug "
39  "runtime mode as it "
40  "contains private API usage.");
41 
42 #define PT_TRACE_ME 0
43 #define PT_SIGEXC 12
44 extern "C" int ptrace(int request, pid_t pid, caddr_t addr, int data);
45 
46 namespace flutter {
47 
48 static bool IsLaunchedByFlutterCLI(const Settings& vm_settings) {
49  // Only the Flutter CLI passes "--enable-checked-mode". Therefore, if the flag
50  // is present, we have been launched by "ios-deploy" via "debugserver".
51  //
52  // We choose this flag because it is always passed to launch debug builds.
53  return vm_settings.enable_checked_mode;
54 }
55 
56 static bool IsLaunchedByXcode() {
57  // Use "sysctl()" to check if we're currently being debugged (e.g. by Xcode).
58  // We could also check "getppid() != 1" (launchd), but this is more direct.
59  const pid_t self = getpid();
60  int mib[5] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, self, 0};
61 
62  auto proc = std::make_unique<struct kinfo_proc>();
63  size_t proc_size = sizeof(struct kinfo_proc);
64  if (::sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) {
65  FML_LOG(ERROR) << "Could not execute sysctl() to get current process info: "
66  << strerror(errno);
67  return false;
68  }
69 
70  return proc->kp_proc.p_flag & P_TRACED;
71 }
72 
73 static bool EnableTracingManually(const Settings& vm_settings) {
74  if (::ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) {
75  FML_LOG(ERROR) << "Could not call ptrace(PT_TRACE_ME): " << strerror(errno);
76  // No use trying PT_SIGEXC -- it's only needed if PT_TRACE_ME succeeds.
77  return false;
78  }
79 
80  if (::ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) {
81  FML_LOG(ERROR) << "Could not call ptrace(PT_SIGEXC): " << strerror(errno);
82  return false;
83  }
84 
85  // The previous operation causes this process to not be reaped after it
86  // terminates (even if PT_SIGEXC fails). Issue a warning to the console every
87  // (approximiately) maxproc/10 leaks. See the links above for an explanation
88  // of this issue.
89  size_t maxproc = 0;
90  size_t maxproc_size = sizeof(size_t);
91  const int sysctl_result =
92  ::sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, nullptr, 0);
93  if (sysctl_result < 0) {
94  FML_LOG(ERROR)
95  << "Could not execute sysctl() to determine process count limit: "
96  << strerror(errno);
97  return false;
98  }
99 
100  const char* warning =
101  "Launching a debug-mode app from the home screen may cause problems.\n"
102  "Please compile a profile-/release-build, launch your app via \"flutter "
103  "run\", or see https://github.com/flutter/flutter/wiki/"
104  "PID-leak-in-iOS-debug-builds-launched-from-home-screen for details.";
105 
106  if (vm_settings.verbose_logging // used for testing and also informative
107  || sysctl_result < 0 // could not determine maximum process count
108  || maxproc / 10 == 0 // avoid division (%) by 0
109  || getpid() % (maxproc / 10) == 0) // warning every ~maxproc/10 leaks
110  {
111  FML_LOG(ERROR) << warning;
112  }
113 
114  return true;
115 }
116 
117 static bool EnableTracingIfNecessaryOnce(const Settings& vm_settings) {
118  if (IsLaunchedByFlutterCLI(vm_settings)) {
119  return true;
120  }
121 
122  if (IsLaunchedByXcode()) {
123  return true;
124  }
125 
126  return EnableTracingManually(vm_settings);
127 }
128 
129 static TracingResult sTracingResult = TracingResult::kNotAttempted;
130 
131 bool EnableTracingIfNecessaryImpl(const Settings& vm_settings) {
132  static std::once_flag tracing_flag;
133 
134  std::call_once(tracing_flag, [&vm_settings]() {
135  sTracingResult = EnableTracingIfNecessaryOnce(vm_settings)
138  });
139  return sTracingResult != TracingResult::kDisabled;
140 }
141 
142 TracingResult GetTracingResultImpl() {
143  return sTracingResult;
144 }
145 
146 } // namespace flutter
147 
148 #endif // TRACING_CHECKS_NECESSARY
#define FML_LOG(severity)
Definition: logging.h:65