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 | #include "flutter/common/settings.h" |
23 | #include "flutter/fml/build_config.h" // For OS_IOS. |
24 | |
25 | #if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) |
26 | |
27 | // These headers should only be needed in debug mode. |
28 | #include <sys/sysctl.h> |
29 | #include <sys/types.h> |
30 | |
31 | #define PT_TRACE_ME 0 |
32 | #define PT_SIGEXC 12 |
33 | extern "C" int ptrace(int request, pid_t pid, caddr_t addr, int data); |
34 | |
35 | static bool DebuggedIOS(const flutter::Settings& vm_settings) { |
36 | // Only the Flutter CLI passes "--enable-checked-mode". Therefore, if the flag |
37 | // is present, we have been launched by "ios-deploy" via "debugserver". |
38 | // |
39 | // We choose this flag because it is always passed to launch debug builds. |
40 | if (vm_settings.enable_checked_mode) { |
41 | return true; |
42 | } |
43 | |
44 | // Use "sysctl()" to check if we're currently being debugged (e.g. by Xcode). |
45 | // We could also check "getppid() != 1" (launchd), but this is more direct. |
46 | const pid_t self = getpid(); |
47 | int mib[5] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, self, 0}; |
48 | |
49 | auto proc = std::make_unique<struct kinfo_proc>(); |
50 | size_t proc_size = sizeof(struct kinfo_proc); |
51 | if (sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) { |
52 | FML_LOG(ERROR) << "Could not execute sysctl() to get current process info: " |
53 | << strerror(errno); |
54 | return false; |
55 | } |
56 | |
57 | return proc->kp_proc.p_flag & P_TRACED; |
58 | } |
59 | |
60 | void EnsureDebuggedIOS(const flutter::Settings& vm_settings) { |
61 | if (DebuggedIOS(vm_settings)) { |
62 | return; |
63 | } |
64 | |
65 | if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) { |
66 | FML_LOG(ERROR) << "Could not call ptrace(PT_TRACE_ME): " << strerror(errno); |
67 | // No use trying PT_SIGEXC -- it's only needed if PT_TRACE_ME succeeds. |
68 | return; |
69 | } |
70 | if (ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) { |
71 | FML_LOG(ERROR) << "Could not call ptrace(PT_SIGEXC): " << strerror(errno); |
72 | } |
73 | |
74 | // The previous operation causes this process to not be reaped after it |
75 | // terminates (even if PT_SIGEXC fails). Issue a warning to the console every |
76 | // (approximiately) maxproc/10 leaks. See the links above for an explanation |
77 | // of this issue. |
78 | size_t maxproc = 0; |
79 | size_t maxproc_size = sizeof(size_t); |
80 | const int sysctl_result = |
81 | sysctlbyname("kern.maxproc" , &maxproc, &maxproc_size, nullptr, 0); |
82 | if (sysctl_result < 0) { |
83 | FML_LOG(ERROR) |
84 | << "Could not execute sysctl() to determine process count limit: " |
85 | << strerror(errno); |
86 | } |
87 | |
88 | const char* warning = |
89 | "Launching a debug-mode app from the home screen may cause problems.\n" |
90 | "Please compile a profile-/release-build, launch your app via \"flutter " |
91 | "run\", or see https://github.com/flutter/flutter/wiki/" |
92 | "PID-leak-in-iOS-debug-builds-launched-from-home-screen for details." ; |
93 | |
94 | if (vm_settings.verbose_logging // used for testing and also informative |
95 | || sysctl_result < 0 // could not determine maximum process count |
96 | || maxproc / 10 == 0 // avoid division (%) by 0 |
97 | || getpid() % (maxproc / 10) == 0) // warning every ~maxproc/10 leaks |
98 | { |
99 | FML_LOG(ERROR) << warning; |
100 | } |
101 | } |
102 | |
103 | #endif // OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG) |
104 | |