Need help resolving a nasty crash

Two Obj-C processes A and B, communicating via XPC, using NSXPCConnection (the connection is created from an endpoint, unnamed).

The method signature is this:

- (void)userAction:(NSString *)identifier
            update:(OITNFWPreventionStage)stage
         eventInfo:(NSDictionary * _Nonnull)actionInfo
         withError:(NSError * _Nullable)error
             reply:(void (^ _Nullable)(BOOL))reply;

I'm using a normal asynchronous proxy

id<myProtocol> monitorProxy = [self.monitorConnection remoteObjectProxyWithErrorHandler:^(NSError * _Nonnull error) {
        NSLog( @"Monitoring XPC proxy call failed: %@", error);
    }];

Since the actionInfo I'm using is NSMutableDictionary the gets updated frequently from concurrent queues and thread - I synchronize ALL my calls from process A to process B on an NSOperationQueue

    NSOperationQueue *monitorUpdateQueue = [[NSOperationQueue alloc] init];
    monitorUpdateQueue.name = @"monitoring queue";
    monitorUpdateQueue.maxConcurrentOperationCount = 1;
    monitorUpdateQueue.qualityOfService = NSQualityOfServiceUtility;

My calls typically look like this:

    [monitorUpdateQueue addOperationWithBlock:^{
        actionInfo[@"Files"] = [fileEvents valueForKeyPath:@"dictionary"]; // some NSArray of NSDictionaries
        actionInfo[@"stage"] = ActionStagePreblocked;
        [monitorProxy userAction:userActionIdentifier update:ActionStagePreblocked eventInfo:actionInfo withError:nil reply:^(BOOL reported) {
            NSLog(@"Action reported");
        }];
    }];

Now every now and then, Process A (the caller) crashes inside this remote call...

I


Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00004c52f8b94400
Exception Codes:       0x0000000000000001, 0x00004c52f8b94400
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    Namespace SIGNAL, Code 11 Segmentation fault: 11
Terminating Process:   exc handler [83282]

VM Region Info: 0x4c52f8b94400 is not in any region.  Bytes after previous region: 83438207583233  Bytes before following region: 21633872346112
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      commpage (reserved)        1000000000-7000000000   [384.0G] ---/--- SM=NUL  ...(unallocated)
--->  GAP OF 0x5f9000000000 BYTES
      MALLOC_NANO              600000000000-600008000000 [128.0M] rw-/rwx SM=PRV  

and the thread's stack looks like this:

Thread 10 Crashed::  Dispatch queue: monitoring queue (QOS: UTILITY)
0   libobjc.A.dylib               	       0x18a7e8310 objc_retain + 16
1   Foundation                    	       0x18b8eb4a8 -[NSDictionary(NSDictionary) encodeWithCoder:] + 596
2   Foundation                    	       0x18b8ba5f4 -[NSXPCEncoder _encodeObject:] + 520
3   Foundation                    	       0x18b8b9ae4 _NSXPCSerializationAddInvocationArgumentsArray + 276
4   Foundation                    	       0x18b8b95fc -[NSXPCEncoder _encodeInvocation:isReply:into:] + 256
5   Foundation                    	       0x18b8b8798 -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 1356
6   CoreFoundation                	       0x18aa08040 ___forwarding___ + 1088
7   CoreFoundation                	       0x18aa07b40 _CF_forwarding_prep_0 + 96
8   myproc                        	       0x10041370c __84-[myproc scanContentOfFilesInEvents:userActionInfo:monitorProxy:monitoringQueue:]_block_invoke_2.1588 + 1064 (myproc.m:3690)
9   Foundation                    	       0x18b8e0600 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24
10  Foundation                    	       0x18b8e04a8 -[NSBlockOperation main] + 104
11  Foundation                    	       0x18b8e0438 __NSOPERATION_IS_INVOKING_MAIN__ + 24
12  Foundation                    	       0x18b8df67c -[NSOperation start] + 804
13  Foundation                    	       0x18b8df350 __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 24
14  Foundation                    	       0x18b8df204 __NSOQSchedule_f + 184
15  libdispatch.dylib             	       0x18a7ad990 _dispatch_block_async_invoke2 + 148
16  libdispatch.dylib             	       0x18a79ebac _dispatch_client_callout + 20
17  libdispatch.dylib             	       0x18a7a2080 _dispatch_continuation_pop + 504
18  libdispatch.dylib             	       0x18a7a16dc _dispatch_async_redirect_invoke + 596
19  libdispatch.dylib             	       0x18a7b031c _dispatch_root_queue_drain + 396
20  libdispatch.dylib             	       0x18a7b0b58 _dispatch_worker_thread2 + 164
21  libsystem_pthread.dylib       	       0x18a959574 _pthread_wqthread + 228
22  libsystem_pthread.dylib       	       0x18a9582c4 start_wqthread + 8

Sorry for the terrible formatting, I could not attach the .ips file, but I attached its full text.

My question: When I'm passing an NSMutableDictionary to the remote proxy. Is it received "mutable" on the other side? and while it is being worked in on the receiving side, what happens if I modify it on the calling side (process A) ?

How do Mutable objects behave on XPC calls?

The Dictionary I'm moving only contains basic "plist approved" entries - NSString, NSNumber, NSDate, and collections (NSArray, NSDictionary). That's all. No custom classes there.

I will be most grateful for any idea or hint.

Accepted Reply

I found the problem to be this: My NSMutableDictionary (actionInfo) sometimes contained values that were of the class NSSet.

Sadly, the Cocoa/ObjC implementation of NSXPCConnection does not allow passing just any type of object "on the wire" to the remote process. I DO NOT completely understand the issue - because SOMETIMES it does work, and sometimes it crashes, but the moment I restrict the objects passed on the wire to those basic Foundation classes one finds in Plists (The sacred NSArray, NSDicationary, NSString, NSDate, NSNumber and (maybe - haven't verified - NSData) The sporadic crashes disappeared completely.

I verified also that all the objects in the NSSet I was passing were from this list of "standard" objects - so - the cause is definitely the NSSet.

What I do now, is pass [mySet allObjects] (yielding an NSArray) to the other side, where I convert it back to [NSSet setWithArray:] and live like that.

Maybe I missed it in the documentation - but I think the only "no no" I have seen in the docs is about classes that can't be securely encoded - and as far as I know NSSet is a honorable and securely-encodable object...

So...

I don't understand, but at least I resolved my crash.

Hope this helps anyone.

Replies

Since the actionInfo I'm using is NSMutableDictionary the gets updated frequently from concurrent queues and thread - I synchronize ALL my calls from process A to process B on an NSOperationQueue

How do you prevent the code running on the operation queue from working with the actionInfo dictionary while the other code, running on “concurrent queues and thread”, is also working with it?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the reply.

Maybe I wasn't clear. In my attempt to resolve the crash, I changed my code so that ALL modifications to the actionInfo mutable dictionary are enclosed in operations added to the serial monitorUpdateQueue queue. You can look at the example - I just have about a dozen "snippets" like that, adding/modifying some info on the actionInfo, then calling the XPC. All on the serial queue. The other concurrent queues and threads, are now ALL dispatching anything related to the actionInfo onto the serial queue.

The whole code related to this mutable dictionary is placed in 2 methods (actionInfo is a heap object, not an object member, allocated in the beginning of one method, and deleted as it exits. I is never passed to other objects -- except, of course, the failing XPC call which "reports" this data to another process.

I found the problem to be this: My NSMutableDictionary (actionInfo) sometimes contained values that were of the class NSSet.

Sadly, the Cocoa/ObjC implementation of NSXPCConnection does not allow passing just any type of object "on the wire" to the remote process. I DO NOT completely understand the issue - because SOMETIMES it does work, and sometimes it crashes, but the moment I restrict the objects passed on the wire to those basic Foundation classes one finds in Plists (The sacred NSArray, NSDicationary, NSString, NSDate, NSNumber and (maybe - haven't verified - NSData) The sporadic crashes disappeared completely.

I verified also that all the objects in the NSSet I was passing were from this list of "standard" objects - so - the cause is definitely the NSSet.

What I do now, is pass [mySet allObjects] (yielding an NSArray) to the other side, where I convert it back to [NSSet setWithArray:] and live like that.

Maybe I missed it in the documentation - but I think the only "no no" I have seen in the docs is about classes that can't be securely encoded - and as far as I know NSSet is a honorable and securely-encodable object...

So...

I don't understand, but at least I resolved my crash.

Hope this helps anyone.