NSTask Explodes. Clueless.

  • In my app I have to launch background processes (bundled within my app's Resources folder) to do some things, and I launch them with NSTask. Every now and then I get a crash report which shows NSTask throws some fatal exception which is not caught by a wrapping @try block.

    For example:

    @try {
    NSString * taskPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"mytask"];
    task = [[NSTask alloc] init];
    task.launchPath = taskPath;
    task.arguments = @[aUUIDString];
    [task launch];
    }
    @catch (NSException * e) {
    ...
    }

    And it crashes:

    Exception Type:  EXC_BREAKPOINT (SIGTRAP)
    Exception Codes: 0x0000000000000002, 0x0000000000000000

    *** NSTask: Task create for path '/Applications/App.app/Contents/Resources/mytask' failed: 22, "Invalid argument". Terminating temporary process.
    *** multi-threaded process forked ***

    Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    0  com.apple.Foundation             0x978142d8 ___NEW_PROCESS_COULD_NOT_BE_EXECD___ + 7
    1  com.apple.Foundation             0x976d2e16 -[NSConcreteTask launchWithDictionary:] + 4698
    2  com.apple.Foundation             0x97744447 -[NSConcreteTask launch] + 40

    What I can't figure out is what the error message means. I imagine it's coming from a call to exec(), but which argument is invalid is beyond me. The path to the task is correct. If the task wasn't in the app bundle, it'd be seriously broken, but also if I try to replicate it by using a bad path, it does crash this way, it simply throws a normal exception which is caught. The arguments I pass to the task are always simple trivial strings, nothing wacky.

    Looking around the open source code for exec(), it appears EINVAL (22) can be returned if the task was exec()'d when not called from a vfork()'d process, with the comment /* If we're not in vfork, don't permit a mutithreaded task to exec */ which seems to line up with the *** multi-threaded process forked *** line in the crash log…  trouble is I have no idea what this ultimately means to me. Seems like an OS bug?

    So:
    - It crashes instead of throwing an exception — not exactly friendly.
    - It crashes *very rarely* (I've never seen it personally.)
    - It's not just one task, it can happen with any of the tasks in the bundle.
    - Seems to only have happened on 10.8.2 so far.

    Any ideas?

    --
    Seth Willits
  • On Feb 4, 2013, at 11:26 AM, Seth Willits <slists...> wrote:
    > Exception Type:  EXC_BREAKPOINT (SIGTRAP)
    > Exception Codes: 0x0000000000000002, 0x0000000000000000
    >
    > *** NSTask: Task create for path '/Applications/App.app/Contents/Resources/mytask' failed: 22, "Invalid argument". Terminating temporary process.
    > *** multi-threaded process forked ***
    >
    > Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    > 0  com.apple.Foundation             0x978142d8 ___NEW_PROCESS_COULD_NOT_BE_EXECD___ + 7
    > 1  com.apple.Foundation             0x976d2e16 -[NSConcreteTask launchWithDictionary:] + 4698
    > 2  com.apple.Foundation             0x97744447 -[NSConcreteTask launch] + 40
    >
    > What I can't figure out is what the error message means. I imagine it's coming from a call to exec(), but which argument is invalid is beyond me. The path to the task is correct. If the task wasn't in the app bundle, it'd be seriously broken, but also if I try to replicate it by using a bad path, it does crash this way, it simply throws a normal exception which is caught. The arguments I pass to the task are always simple trivial strings, nothing wacky.
    >
    > Looking around the open source code for exec(), it appears EINVAL (22) can be returned if the task was exec()'d when not called from a vfork()'d process, with the comment /* If we're not in vfork, don't permit a mutithreaded task to exec */ which seems to line up with the *** multi-threaded process forked *** line in the crash log…  trouble is I have no idea what this ultimately means to me. Seems like an OS bug?

    Does your crash log show multiple threads? I would expect that the crash log shows only one thread because NSTask called fork() already. That in turn would suggest that the EINVAL is coming from somewhere else.

    NSTask's implementation might also be using fork() + posix_spawn() in your OS version. You should look for possible EINVAL errors from posix_spawn().

    > - It crashes instead of throwing an exception — not exactly friendly.

    The error occurred in the child process on the other side of the fork(). Note that your app didn't crash as far as the user could see; only the new child crashed. It would be difficult to communicate a post-fork() error to the parent process in order to throw an exception in the parent.

    --
    Greg Parker    <gparker...>    Runtime Wrangler
  • > Does your crash log show multiple threads? I would expect that the crash log shows only one thread because NSTask called fork() already.

    Hmm. Yes, there's only one thread. I didn't notice that. That explains the crash (of the forked process) "instead of an exception," like you mentioned.

    > That in turn would suggest that the EINVAL is coming from somewhere else.
    > NSTask's implementation might also be using fork() + posix_spawn() in your OS version. You should look for possible EINVAL errors from posix_spawn().

    Looking at the manpage, EINVAL can be thrown because "The value specified by file_actions or attrp is invalid." I looked at both of those and both appear completely beyond my influence, so I don't know what could cause it.

    --
    Seth Willits
  • On Feb 5, 2013, at 10:39 AM, Seth Willits <slists...> wrote:

    >> Does your crash log show multiple threads? I would expect that the crash log shows only one thread because NSTask called fork() already.
    >
    > Hmm. Yes, there's only one thread. I didn't notice that. That explains the crash (of the forked process) "instead of an exception," like you mentioned.

    One thing I want to point out, though, is that the crash is an exception. It's just a Mach exception, not an Objective-C exception.

    That's why it's named EXC_BREAKPOINT: it's the Mach exception signaled when a breakpoint instruction is executed. But OS-level exceptions are completely independent of language-level exceptions on most platforms, so you can't expect to catch them with language-level constructs.

    Of course, unless you're dealing with a few very specialized cases, you shouldn't need to figure out how to handle Mach exceptions in your own code. They signify extremely serious errors, ones you're almost certainly unlikely to be able to recover from.

      -- Chris
  • On Feb 4, 2013, at 11:26 AM, Seth Willits <slists...> wrote:

    > Looking around the open source code for exec(), it appears EINVAL (22) can be returned if the task was exec()'d when not called from a vfork()'d process, with the comment /* If we're not in vfork, don't permit a mutithreaded task to exec */ which seems to line up with the *** multi-threaded process forked *** line in the crash log…  trouble is I have no idea what this ultimately means to me. Seems like an OS bug?

    One thing that occurs to me is that there might be something running that is injecting itself into your app's address space - and into the address space of the forked subprocess, and trying to do something "on startup."

    What loaded libraries are listed in the crash dump?

      -- Chris
  • On 2/4/13 8:26 PM, Seth Willits wrote:
    > [snip]
    > Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    > 0  com.apple.Foundation             0x978142d8 ___NEW_PROCESS_COULD_NOT_BE_EXECD___ + 7
    > 1  com.apple.Foundation             0x976d2e16 -[NSConcreteTask launchWithDictionary:] + 4698
    > 2  com.apple.Foundation             0x97744447 -[NSConcreteTask launch] + 40
    > [/snip]
    Hi Seth,

    are you starting a shell script here or native binary?

    BR,
    Ivan
  • On Feb 6, 2013, at 10:29 PM, Chris Hanson wrote:

    > On Feb 4, 2013, at 11:26 AM, Seth Willits <slists...> wrote:
    >
    >> Looking around the open source code for exec(), it appears EINVAL (22) can be returned if the task was exec()'d when not called from a vfork()'d process, with the comment /* If we're not in vfork, don't permit a mutithreaded task to exec */ which seems to line up with the *** multi-threaded process forked *** line in the crash log…  trouble is I have no idea what this ultimately means to me. Seems like an OS bug?
    >
    > One thing that occurs to me is that there might be something running that is injecting itself into your app's address space - and into the address space of the forked subprocess, and trying to do something "on startup."
    >
    > What loaded libraries are listed in the crash dump?

    Nothing third party that I can see.

    https://gist.github.com/anonymous/4732383/

    On Feb 7, 2013, at 3:40 AM, Ivan Ostres wrote:

    > On 2/4/13 8:26 PM, Seth Willits wrote:
    >> [snip]
    >> Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    >> 0  com.apple.Foundation             0x978142d8 ___NEW_PROCESS_COULD_NOT_BE_EXECD___ + 7
    >> 1  com.apple.Foundation             0x976d2e16 -[NSConcreteTask launchWithDictionary:] + 4698
    >> 2  com.apple.Foundation             0x97744447 -[NSConcreteTask launch] + 40
    >> [/snip]
    > Hi Seth,
    >
    > are you starting a shell script here or native binary?

    A native 32-bit binary.

    --
    Seth Willits
  • Update: I *finally* got a bead on this.

    The user running my main application has parental controls enabled. The user is allowed to run the main application, and can, but the helper task that is bundled inside the main application's bundle is not allowed to run. When the *task* launches, *it* crashes, but since it happens *while* fork/exec'ing, the crash *appears* to be main application crashing, but it's not.

    When the task launches, the user is shown a dialog by the OS: "You don't have permission to use the application "mytask."" There are options to Always allow, allow once, and just ignore and move on. Allowing once, and even "Always allow" always results in the same message, and the task crashing. The permissions do not stick at all, probably because it's bundled inside the main .app package's Resource folder?

    Any ideas what to do?

    --
    Seth Willits

    On Feb 4, 2013, at 11:26 AM, Seth Willits wrote:

    >
    > In my app I have to launch background processes (bundled within my app's Resources folder) to do some things, and I launch them with NSTask. Every now and then I get a crash report which shows NSTask throws some fatal exception which is not caught by a wrapping @try block.
    >
    >
    > For example:
    >
    >
    >
    > @try {
    > NSString * taskPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"mytask"];
    > task = [[NSTask alloc] init];
    > task.launchPath = taskPath;
    > task.arguments = @[aUUIDString];
    > [task launch];
    > }
    > @catch (NSException * e) {
    > ...
    > }
    >
    >
    >
    > And it crashes:
    >
    >
    > Exception Type:  EXC_BREAKPOINT (SIGTRAP)
    > Exception Codes: 0x0000000000000002, 0x0000000000000000
    >
    > *** NSTask: Task create for path '/Applications/App.app/Contents/Resources/mytask' failed: 22, "Invalid argument". Terminating temporary process.
    > *** multi-threaded process forked ***
    >
    > Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
    > 0  com.apple.Foundation             0x978142d8 ___NEW_PROCESS_COULD_NOT_BE_EXECD___ + 7
    > 1  com.apple.Foundation             0x976d2e16 -[NSConcreteTask launchWithDictionary:] + 4698
    > 2  com.apple.Foundation             0x97744447 -[NSConcreteTask launch] + 40
    >

    >

    --
    Seth Willits
  • On May 12, 2013, at 8:08 PM, Seth Willits wrote:

    > The user running my main application has parental controls enabled. The user is allowed to run the main application, and can, but the helper task that is bundled inside the main application's bundle is not allowed to run. When the *task* launches, *it* crashes, but since it happens *while* fork/exec'ing, the crash *appears* to be main application crashing, but it's not.
    >
    > When the task launches, the user is shown a dialog by the OS: "You don't have permission to use the application "mytask."" There are options to Always allow, allow once, and just ignore and move on. Allowing once, and even "Always allow" always results in the same message, and the task crashing. The permissions do not stick at all, probably because it's bundled inside the main .app package's Resource folder?
    >
    > Any ideas what to do?

    Is your helper task separately code-signed?  It should be.

    Regards,
    Ken
  • On May 12, 2013, at 6:47 PM, Ken Thomases wrote:

    > On May 12, 2013, at 8:08 PM, Seth Willits wrote:
    >
    >> The user running my main application has parental controls enabled. The user is allowed to run the main application, and can, but the helper task that is bundled inside the main application's bundle is not allowed to run. When the *task* launches, *it* crashes, but since it happens *while* fork/exec'ing, the crash *appears* to be main application crashing, but it's not.
    >>
    >> When the task launches, the user is shown a dialog by the OS: "You don't have permission to use the application "mytask."" There are options to Always allow, allow once, and just ignore and move on. Allowing once, and even "Always allow" always results in the same message, and the task crashing. The permissions do not stick at all, probably because it's bundled inside the main .app package's Resource folder?
    >>
    >> Any ideas what to do?
    >
    > Is your helper task separately code-signed?  It should be.

    Yep. Always, and with the same identity as the main app.

    --
    Seth Willits
previous month february 2013 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28      
Go to today