diff --git a/infer/src/pulse/PulseModels.ml b/infer/src/pulse/PulseModels.ml index 7414a7cd7..4bc0c2d33 100644 --- a/infer/src/pulse/PulseModels.ml +++ b/infer/src/pulse/PulseModels.ml @@ -261,7 +261,7 @@ module ObjC = struct location callee_proc_name ~ret ~actuals:[] ~formals_opt:None astate ) - let insertion_into_dictionary (value, value_hist) (key, key_hist) ~desc : model = + let insertion_into_collection_key_and_value (value, value_hist) (key, key_hist) ~desc : model = fun {location} astate -> let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let<*> astate, _ = @@ -277,7 +277,7 @@ module ObjC = struct astate - let insertion_into_array (value, value_hist) ~desc : model = + let insertion_into_collection_key_or_value (value, value_hist) ~desc : model = fun {location} astate -> let event = ValueHistory.Call {f= Model desc; location; in_call= []} in let<+> astate, _ = @@ -1746,16 +1746,23 @@ module ProcNameDispatcher = struct ; -"NSObject" &:: "init" <>$ capt_arg_payload $--> Misc.id_first_arg ~desc:"NSObject.init" ; +map_context_tenv (PatternMatch.ObjectiveC.implements "NSMutableDictionary") &:: "setObject:forKey:" <>$ any_arg $+ capt_arg_payload $+ capt_arg_payload - $--> ObjC.insertion_into_dictionary ~desc:"NSMutableDictionary.setObject:forKey:" + $--> ObjC.insertion_into_collection_key_and_value + ~desc:"NSMutableDictionary.setObject:forKey:" + ; +map_context_tenv (PatternMatch.ObjectiveC.implements "NSMutableDictionary") + &:: "setObject:forKeyedSubscript:" <>$ any_arg $+ any_arg $+ capt_arg_payload + $--> ObjC.insertion_into_collection_key_or_value + ~desc:"mutableDictionary[someKey] = value" ; +map_context_tenv (PatternMatch.ObjectiveC.implements "NSMutableArray") &:: "addObject:" <>$ any_arg $+ capt_arg_payload - $--> ObjC.insertion_into_array ~desc:"NSMutableArray.addObject:" + $--> ObjC.insertion_into_collection_key_or_value ~desc:"NSMutableArray.addObject:" ; +map_context_tenv (PatternMatch.ObjectiveC.implements "NSMutableArray") &:: "insertObject:atIndex:" <>$ any_arg $+ capt_arg_payload $+ any_arg - $--> ObjC.insertion_into_array ~desc:"NSMutableArray.insertObject:atIndex:" + $--> ObjC.insertion_into_collection_key_or_value + ~desc:"NSMutableArray.insertObject:atIndex:" ; +map_context_tenv (PatternMatch.ObjectiveC.implements "NSMutableArray") &:: "replaceObjectAtIndex:withObject:" <>$ any_arg $+ any_arg $+ capt_arg_payload - $--> ObjC.insertion_into_array ~desc:"NSMutableArray.replaceObjectAtIndex:withObject:" + $--> ObjC.insertion_into_collection_key_or_value + ~desc:"NSMutableArray.replaceObjectAtIndex:withObject:" ; +match_regexp_opt Config.pulse_model_return_nonnull &::.*--> Misc.return_positive ~desc:"modelled as returning not null due to configuration option" diff --git a/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm b/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm index 4be9e8d1f..916ff5928 100644 --- a/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm +++ b/infer/tests/codetoanalyze/objcpp/pulse/NPEBasic.mm @@ -193,6 +193,20 @@ void addObjectInDict(NSMutableDictionary* mDict, id value) { [mDict setObject:value forKey:@"somestring"]; } +void addNilInDictBracketsOk(NSMutableDictionary* mDict) { + mDict[@"key2"] = nil; // Passing nil will cause any object corresponding + // to a key to be removed from the dictionary. +} + +void addNilKeyInDictBracketsBad(NSMutableDictionary* mDict) { + id key = nil; + mDict[key] = @"somestring"; +} + +void addInDictBracketsOk(NSMutableDictionary* mDict) { + mDict[@"key"] = @"somestring"; +} + void testNilMessagingForModelNilNilOK_FP() { addObjectInDict(nil, nil); } void testNilMessagingForModelNilStringOK() { @@ -224,3 +238,15 @@ void replaceNilInArrayBad(NSMutableArray* mArray) { void replaceObjectInArrayOk(NSMutableArray* mArray) { [mArray replaceObjectAtIndex:0 withObject:[SomeObject new]]; } + +void addInDictBracketsDefault(NSMutableDictionary* mDict, + NSString* key) { + mDict[key] = @"default"; +} + +void accessZeroElementOk_FP(NSMutableDictionary* mDict) { + NSArray* array = + [[NSUserDefaults standardUserDefaults] arrayForKey:@"key"]; + NSString* key = array[0]; + addInDictBracketsDefault(mDict, key); +} diff --git a/infer/tests/codetoanalyze/objcpp/pulse/issues.exp b/infer/tests/codetoanalyze/objcpp/pulse/issues.exp index 9049dd91e..f69e0e73e 100644 --- a/infer/tests/codetoanalyze/objcpp/pulse/issues.exp +++ b/infer/tests/codetoanalyze/objcpp/pulse/issues.exp @@ -1,6 +1,8 @@ codetoanalyze/objcpp/pulse/AllocPatternMemLeak.mm, A.create_no_release_leak_bad, 2, MEMORY_LEAK, no_bucket, ERROR, [allocation part of the trace starts here,allocated by call to `ABFDataCreate` (modelled),allocation part of the trace ends here,memory becomes unreachable here] +codetoanalyze/objcpp/pulse/NPEBasic.mm, accessZeroElementOk_FP, 4, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [source of the null value part of the trace starts here,assigned,is the null pointer,null pointer dereference part of the trace starts here,assigned,passed as argument to `NSArray.objectAtIndexedSubscript:`,return from call to `NSArray.objectAtIndexedSubscript:`,assigned,when calling `addInDictBracketsDefault` here,parameter `key` of addInDictBracketsDefault,passed as argument to `mutableDictionary[someKey] = value` (modelled),return from call to `mutableDictionary[someKey] = value` (modelled),invalid access occurs here] codetoanalyze/objcpp/pulse/NPEBasic.mm, addNilInArrayBad, 0, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [is the null pointer,passed as argument to `NSMutableArray.addObject:` (modelled),return from call to `NSMutableArray.addObject:` (modelled),invalid access occurs here] codetoanalyze/objcpp/pulse/NPEBasic.mm, addNilInDictBad, 2, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [is the null pointer,assigned,passed as argument to `NSMutableDictionary.setObject:forKey:` (modelled),return from call to `NSMutableDictionary.setObject:forKey:` (modelled),invalid access occurs here] +codetoanalyze/objcpp/pulse/NPEBasic.mm, addNilKeyInDictBracketsBad, 2, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [is the null pointer,assigned,passed as argument to `mutableDictionary[someKey] = value` (modelled),return from call to `mutableDictionary[someKey] = value` (modelled),invalid access occurs here] codetoanalyze/objcpp/pulse/NPEBasic.mm, addObjectKeyNilInDictBad, 2, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [is the null pointer,passed as argument to `NSMutableDictionary.setObject:forKey:` (modelled),return from call to `NSMutableDictionary.setObject:forKey:` (modelled),invalid access occurs here] codetoanalyze/objcpp/pulse/NPEBasic.mm, dereferenceNilBad, 2, NULLPTR_DEREFERENCE, no_bucket, ERROR, [is the null pointer,assigned,invalid access occurs here] codetoanalyze/objcpp/pulse/NPEBasic.mm, insertNilInArrayBad, 1, NIL_INSERTION_INTO_COLLECTION, no_bucket, ERROR, [is the null pointer,passed as argument to `NSMutableArray.insertObject:atIndex:` (modelled),return from call to `NSMutableArray.insertObject:atIndex:` (modelled),invalid access occurs here]