Skip to content

Commit

Permalink
Ensure debug-mode apps are always attached on iOS. (flutter#10186)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjindel-google authored Aug 5, 2019
1 parent 5dd04df commit d6a02a3
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 1 deletion.
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,8 @@ FILE: ../../../flutter/runtime/dart_vm_unittests.cc
FILE: ../../../flutter/runtime/embedder_resources.cc
FILE: ../../../flutter/runtime/embedder_resources.h
FILE: ../../../flutter/runtime/fixtures/runtime_test.dart
FILE: ../../../flutter/runtime/ptrace_ios.cc
FILE: ../../../flutter/runtime/ptrace_ios.h
FILE: ../../../flutter/runtime/runtime_controller.cc
FILE: ../../../flutter/runtime/runtime_controller.h
FILE: ../../../flutter/runtime/runtime_delegate.cc
Expand Down
1 change: 1 addition & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ struct Settings {
std::vector<std::string> dart_entrypoint_args;

// Isolate settings
bool enable_checked_mode = false;
bool start_paused = false;
bool trace_skia = false;
bool trace_startup = false;
Expand Down
2 changes: 2 additions & 0 deletions runtime/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ source_set("runtime") {
"dart_vm_lifecycle.h",
"embedder_resources.cc",
"embedder_resources.h",
"ptrace_ios.cc",
"ptrace_ios.h",
"runtime_controller.cc",
"runtime_controller.h",
"runtime_delegate.cc",
Expand Down
7 changes: 6 additions & 1 deletion runtime/dart_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "flutter/lib/ui/dart_ui.h"
#include "flutter/runtime/dart_isolate.h"
#include "flutter/runtime/dart_service_isolate.h"
#include "flutter/runtime/ptrace_ios.h"
#include "flutter/runtime/start_up.h"
#include "third_party/dart/runtime/include/bin/dart_io_api.h"
#include "third_party/tonic/converter/dart_converter.h"
Expand Down Expand Up @@ -307,11 +308,15 @@ DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
}
#endif // !OS_FUCHSIA

#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
#if (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
#if !OS_IOS || TARGET_OS_SIMULATOR
// Debug mode uses the JIT, disable code page write protection to avoid
// memory page protection changes before and after every compilation.
PushBackAll(&args, kDartWriteProtectCodeArgs,
fml::size(kDartWriteProtectCodeArgs));
#else
EnsureDebuggedIOS(settings_);
#endif
#endif

if (enable_asserts) {
Expand Down
103 changes: 103 additions & 0 deletions runtime/ptrace_ios.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// It is __imperative__ that the functions in this file are __not__ included in
// release or profile builds.
//
// They call into the "private" ptrace() API to ensure that the current process
// is being ptrace()-d. Only debug builds rely on ptrace(), and the ptrace() API
// is not allowed for use in the App Store, so we must exclude it from profile-
// and release-builds.
//
// When an app is launched from a host workstation (e.g. via Xcode or
// "ios-deploy"), the process is already ptrace()-d by debugserver. However,
// when an app is launched from the home screen, it is not, so for debug builds
// we initialize the ptrace() relationship via PT_TRACE_ME if necessary.
//
// Please see the following documents for more details:
// - go/decommissioning-dbc
// - go/decommissioning-dbc-engine
// - go/decommissioning-dbc-tools
#include "flutter/common/settings.h"
#include "flutter/fml/build_config.h" // For OS_IOS.

#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)

// These headers should only be needed in debug mode.
#include <sys/sysctl.h>
#include <sys/types.h>

#define PT_TRACE_ME 0
#define PT_SIGEXC 12
extern "C" int ptrace(int request, pid_t pid, caddr_t addr, int data);

static bool DebuggedIOS(const flutter::Settings& vm_settings) {
// Only the Flutter CLI passes "--enable-checked-mode". Therefore, if the flag
// is present, we have been launched by "ios-deploy" via "debugserver".
//
// We choose this flag because it is always passed to launch debug builds.
if (vm_settings.enable_checked_mode) {
return true;
}

// Use "sysctl()" to check if we're currently being debugged (e.g. by Xcode).
// We could also check "getppid() != 1" (launchd), but this is more direct.
const pid_t self = getpid();
int mib[5] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, self, 0};

auto proc = std::make_unique<struct kinfo_proc>();
size_t proc_size = sizeof(struct kinfo_proc);
if (sysctl(mib, 4, proc.get(), &proc_size, nullptr, 0) < 0) {
FML_LOG(ERROR) << "Could not execute sysctl() to get current process info: "
<< strerror(errno);
return false;
}

return proc->kp_proc.p_flag & P_TRACED;
}

void EnsureDebuggedIOS(const flutter::Settings& vm_settings) {
if (DebuggedIOS(vm_settings)) {
return;
}

if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1) {
FML_LOG(ERROR) << "Could not call ptrace(PT_TRACE_ME): " << strerror(errno);
// No use trying PT_SIGEXC -- it's only needed if PT_TRACE_ME succeeds.
return;
}
if (ptrace(PT_SIGEXC, 0, nullptr, 0) == -1) {
FML_LOG(ERROR) << "Could not call ptrace(PT_SIGEXC): " << strerror(errno);
}

// The previous operation causes this process to not be reaped after it
// terminates (even if PT_SIGEXC fails). Issue a warning to the console every
// (approximiately) maxproc/10 leaks. See the links above for an explanation
// of this issue.
size_t maxproc = 0;
size_t maxproc_size = sizeof(size_t);
const int sysctl_result =
sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, nullptr, 0);
if (sysctl_result < 0) {
FML_LOG(ERROR)
<< "Could not execute sysctl() to determine process count limit: "
<< strerror(errno);
}

const char* warning =
"Launching a debug-mode app from the home screen may cause problems.\n"
"Please compile a profile-/release-build, launch your app via \"flutter "
"run\", or see https://github.com/flutter/flutter/wiki/"
"PID-leak-in-iOS-debug-builds-launched-from-home-screen for details.";

if (vm_settings.verbose_logging // used for testing and also informative
|| sysctl_result < 0 // could not determine maximum process count
|| maxproc / 10 == 0 // avoid division (%) by 0
|| getpid() % (maxproc / 10) == 0) // warning every ~maxproc/10 leaks
{
FML_LOG(ERROR) << warning;
}
}

#endif // OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)
18 changes: 18 additions & 0 deletions runtime/ptrace_ios.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_RUNTIME_PTRACE_IOS_H_
#define FLUTTER_RUNTIME_PTRACE_IOS_H_

#include "flutter/common/settings.h"

#if OS_IOS && (FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG)

// Ensure that the current process is or was ptrace()-d at some point in its
// life. Can only be used within debug builds for iOS.
void EnsureDebuggedIOS(const flutter::Settings& vm_settings);

#endif

#endif // FLUTTER_RUNTIME_PTRACE_IOS_H_
3 changes: 3 additions & 0 deletions shell/common/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.start_paused =
command_line.HasOption(FlagForSwitch(Switch::StartPaused));

settings.enable_checked_mode =
command_line.HasOption(FlagForSwitch(Switch::EnableCheckedMode));

settings.enable_dart_profiling =
command_line.HasOption(FlagForSwitch(Switch::EnableDartProfiling));

Expand Down
1 change: 1 addition & 0 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ DEF_SWITCH(DisableServiceAuthCodes,
DEF_SWITCH(StartPaused,
"start-paused",
"Start the application paused in the Dart debugger.")
DEF_SWITCH(EnableCheckedMode, "enable-checked-mode", "Enable checked mode.")
DEF_SWITCH(TraceStartup,
"trace-startup",
"Trace early application lifecycle. Automatically switches to an "
Expand Down

0 comments on commit d6a02a3

Please sign in to comment.