Page 1 of 2

Catching memory crashes

PostPosted: Fri Oct 26, 2018 4:01 pm
by cahoots
Hi, I'm trying to find a good way to catch memory crashes, and the best way I know of (https://code.fb.com/ios/reducing-fooms- ... k-ios-app/) doesn't really do the job. For other crashes, it's easy enough to catch it with signal handlers, but with memory crashes we don't get a signal we can intercept. Is there some crazy way to determine if the app's last session terminated due to a memory crash? I'm most interested in this for iOS, but anything would help.

Re: Catching memory crashes

PostPosted: Sun Oct 28, 2018 8:36 pm
by morpheus
You actually DO get a signal you can intercept. As explained in detail in Chapter 9. The FB approach in the article isn't the right way to do it, and the article is quite inaccurate . TL;DR: There is more than one way to react to memory pressure, before you get SIGKILLed by jetsam. Rather than explain here, just read the relevant pages:

283.png
283.png (587.85 KiB) Viewed 3617 times


284.png
284.png (557.45 KiB) Viewed 3617 times


285.png
285.png (586.88 KiB) Viewed 3617 times



And mental note to myself: iOS 12 now has /usr/libexec/ReportMemoryException.

Re: Catching memory crashes

PostPosted: Mon Oct 29, 2018 9:23 pm
by cahoots
Thanks, that makes sense. However, when I try it, kevent returns immediately, adds EV_ERROR to the flags for eventEv, and sets ENOTSUP for the data for eventEv. FYI I'm using an iPhone 8 with iOS 12.0 (16A366). Code:
Code: Select all
    struct kevent changeEv = {0};
    changeEv.filter = EVFILT_VM;
    changeEv.flags = EV_ADD | EV_ENABLE;
    changeEv.fflags = NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
    struct kevent eventEv = {0};
    int n = kevent(kq, &changeEv, 1, &eventEv, 1, NULL);
    assert(n > 0 && !(eventEv.flags & EV_ERROR));
    close(kq);

Re: Catching memory crashes

PostPosted: Tue Oct 30, 2018 11:19 pm
by morpheus
you're using it wrong; There's an example on how to do the kevent loop in the 1st ed. First set, then read. Also look at XNU's side of things (somewhere in bsd/kern - not near the source right now so can't tell)

Re: Catching memory crashes

PostPosted: Wed Oct 31, 2018 11:06 pm
by cahoots
Ok, I've tried the example from the first book after replacing the pid with 0, EVFILT_PROC with EVFILT_VM, and NOTE_EXIT | NOTE_FORK | NOTE_EXEC with NOTE_VM_PRESSURE_SUDDEN_TERMINATE. Unfortunately, it still doesn't work, giving ENOTSUP when I run it. Any ideas?

Code: Select all
int main (int argc, char **argv)
{
    pid_t pid; // PID to monitor
    int kq; // The kqueue file descriptor
    int rc; // collecting return values
    int done;
    struct kevent ke;
    pid = atoi(argv[1]);
    kq = kqueue();
    if (kq == -1) { perror("kqueue"); exit(2); }
    // Set process fork/exec notifications
    EV_SET(&ke, 0, EVFILT_VM, EV_ADD,
           NOTE_VM_PRESSURE_SUDDEN_TERMINATE, 0, NULL);
    // Register event
    rc = kevent(kq, &ke, 1, NULL, 0, NULL);
    if (rc < 0) { perror ("kevent"); exit (3); }
    done = 0;
    while (!done) {
        memset(&ke, '\0', sizeof(struct kevent));
        // This blocks until an event matching the filter occurs
        rc = kevent(kq, NULL, 0, &ke, 1, NULL);
        if (rc < 0) { perror ("kevent"); exit (4); }
    } // end while

    NSLog(@"fail");

    return 0;
}

Re: Catching memory crashes

PostPosted: Tue Nov 06, 2018 11:23 pm
by cahoots
This would be a huge win for iOS developers if it actually works

Re: Catching memory crashes

PostPosted: Sat Nov 17, 2018 10:26 pm
by septium
High-level way of doing it:

On the jailbroken iDevice you can hook into [SBMainWorkspace processDidExit:] call in SpringBoard to monitor terminations and query for details.

Code: Select all
void _hooked_SBMainWorkspace_processDidExit_impl(id self, SEL cmd, id process) {
    _orig_SBMainWorkspace_processDidExit_impl(self, cmd, process);
    if ([process isKindOfClass:objc_getClass("FBApplicationProcess")]) { //theoretically it can be FBProcess, without exitContext
        NSString *bundleID = [process bundleIdentifier];
        FBApplicationProcessExitContext *exitContext = [process exitContext];
        //look for details
        //[exitContext consideredJetsam]; //YES on OOM crash
        //[exitContext fairPlayFailure];
        //[exitContext terminationSignal];
        //[exitContext exitCode];
    }
}


Don't forget to include relevant private headers, or just declare methods in private category of NSObject.

http://developer.limneos.net/?ios=11.1.2&framework=SpringBoard&header=SBMainWorkspace.h
http://developer.limneos.net/?ios=11.1.2&framework=FrontBoard.framework&header=FBApplicationProcess.h
http://developer.limneos.net/?ios=11.1.2&framework=FrontBoard.framework&header=FBApplicationProcessExitContext.h

Re: Catching memory crashes

PostPosted: Sun Nov 18, 2018 8:20 pm
by cahoots
septium, thanks for the advice. My goal here is to find a way to do this for non-jailbroken devices, ideally without private API usage but ok if necessary.

Re: Catching memory crashes

PostPosted: Mon Nov 19, 2018 9:22 am
by septium
So you want to say that kevent on EVFILT_VM/NOTE_VM_PRESSURE_SUDDEN_TERMINATE will work from non-entitled sandboxed process? This would be a huge luck and huge miss from Apple if the answer is yes.

Re: Catching memory crashes

PostPosted: Mon Nov 19, 2018 3:01 pm
by LowellSti
Why do you say that, Septium? You think it's not meant to work like that?