• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DebuggedIOS(const flutter::Settings & vm_settings)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 
EnsureDebuggedIOS(const flutter::Settings & vm_settings)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