Improve stability on non-jailbroken devices

By setting up a Mach exception handler, we can try to handle most
signals before they cause the system to hang. Since PT_TRACE_ME enables
CS_DEBUGGED on the parent process as well, we can simplify some of the
code as well.
This commit is contained in:
Saagar Jha 2020-02-23 01:27:25 -08:00
parent 6610cd7663
commit 99c631c791
3 changed files with 51 additions and 112 deletions

View File

@ -105,4 +105,8 @@
NativeMessageReceived("got_focus", "");
}
- (void)applicationWillTerminate:(UIApplication *)application {
exit(0);
}
@end

View File

@ -1,65 +0,0 @@
/*
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
#ifndef _SYS_CODESIGN_H_
#define _SYS_CODESIGN_H_
#include <sys/types.h>
/* code signing attributes of a process */
#define CS_VALID 0x0001 /* dynamically valid */
#define CS_HARD 0x0100 /* don't load invalid pages */
#define CS_KILL 0x0200 /* kill process if it becomes invalid */
#define CS_EXEC_SET_HARD 0x1000 /* set CS_HARD on any exec'ed process */
#define CS_EXEC_SET_KILL 0x2000 /* set CS_KILL on any exec'ed process */
#define CS_KILLED 0x10000 /* was killed by kernel for invalidity */
#define CS_RESTRICT 0x20000 /* tell dyld to treat restricted */
/* csops operations */
#define CS_OPS_STATUS 0 /* return status */
#define CS_OPS_MARKINVALID 1 /* invalidate process */
#define CS_OPS_MARKHARD 2 /* set HARD flag */
#define CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */
#define CS_OPS_PIDPATH 4 /* get executable's pathname */
#define CS_OPS_CDHASH 5 /* get code directory hash */
#define CS_OPS_PIDOFFSET 6 /* get offset of active Mach-o slice */
#define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */
#define CS_OPS_MARKRESTRICT 8 /* set RESTRICT flag (sticky) */
#ifndef KERNEL
__BEGIN_DECLS
/* code sign operations */
int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
__END_DECLS
#endif /* ! KERNEL */
#endif /* _SYS_CODESIGN_H_ */

View File

@ -1,12 +1,14 @@
// main.mm boilerplate
#import <UIKit/UIKit.h>
#import <dlfcn.h>
#import <mach/mach.h>
#import <pthread.h>
#import <string>
#import <stdio.h>
#import <stdlib.h>
#import <sys/syscall.h>
#import <AudioToolbox/AudioToolbox.h>
#import "codesign.h"
#import "AppDelegate.h"
#import "PPSSPPUIApplication.h"
@ -15,53 +17,33 @@
#include "base/NativeApp.h"
#include "profiler/profiler.h"
#define CS_OPS_STATUS 0 /* return status */
#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
#define PTRACE_TRACEME 0 /* Indicate that this process is to be traced by its parent. */
#define PT_ATTACHEXC 14 /* attach to running process with signal exception */
#define PT_DETACH 11 /* stop tracing a process */
int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
#define ptrace(a, b, c, d) syscall(SYS_ptrace, a, b, c, d)
#define CS_OPS_STATUS 0 /* return status */
#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */
#define PT_TRACE_ME 0 /* child declares it's being traced */
#define PT_SIGEXC 12 /* signals as exceptions for current_proc */
bool get_debugged() {
static int (*csops)(pid_t pid, unsigned int ops, void * useraddr, size_t usersize);
static boolean_t (*exc_server)(mach_msg_header_t *, mach_msg_header_t *);
static int (*ptrace)(int request, pid_t pid, caddr_t addr, int data);
bool cs_debugged() {
int flags;
int rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags));
if (rv==0 && flags&CS_DEBUGGED) return true;
return !csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) && flags & CS_DEBUGGED;
}
pid_t pid = fork();
if (pid > 0) {
int st,rv,i=0;
do {
usleep(500);
rv = waitpid(pid, &st, 0);
} while (rv<0 && i++<10);
if (rv<0) fprintf(stderr, "Unable to wait for child?\n");
} else if (pid == 0) {
pid_t ppid = getppid();
int rv = ptrace(PT_ATTACHEXC, ppid, 0, 0);
if (rv) {
perror("Unable to attach to process");
exit(1);
}
for (int i=0; i<100; i++) {
usleep(1000);
errno = 0;
rv = ptrace(PT_DETACH, ppid, 0, 0);
if (rv==0) break;
}
if (rv) {
perror("Unable to detach from process");
exit(1);
}
exit(0);
} else {
perror("Unable to fork");
}
kern_return_t catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t code_count) {
return KERN_FAILURE;
}
rv = csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags));
if (rv==0 && flags&CS_DEBUGGED) return true;
return false;
void *exception_handler(void *argument) {
auto port = *reinterpret_cast<mach_port_t *>(argument);
mach_msg_server(exc_server, 2048, port, 0);
return NULL;
}
@ -185,10 +167,28 @@ void Vibrate(int mode) {
int main(int argc, char *argv[])
{
csops = reinterpret_cast<decltype(csops)>(dlsym(dlopen(nullptr, RTLD_LAZY), "csops"));
exc_server = reinterpret_cast<decltype(exc_server)>(dlsym(dlopen(NULL, RTLD_LAZY), "exc_server"));
ptrace = reinterpret_cast<decltype(ptrace)>(dlsym(dlopen(NULL, RTLD_LAZY), "ptrace"));
// see https://github.com/hrydgard/ppsspp/issues/11905
if (!get_debugged()) {
fprintf(stderr, "Unable to cleanly obtain CS_DEBUGGED - probably not jailbroken. Attempting old method.\n");
ptrace(PTRACE_TRACEME, 0, 0, 0);
if (!cs_debugged()) {
pid_t pid = fork();
if (pid == 0) {
ptrace(PT_TRACE_ME, 0, nullptr, 0);
exit(0);
} else if (pid < 0) {
perror("Unable to fork");
ptrace(PT_TRACE_ME, 0, nullptr, 0);
ptrace(PT_SIGEXC, 0, nullptr, 0);
mach_port_t port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_SOFTWARE, port, EXCEPTION_DEFAULT, THREAD_STATE_NONE);
pthread_t thread;
pthread_create(&thread, nullptr, exception_handler, reinterpret_cast<void *>(&port));
}
}
PROFILE_INIT();