diff --git a/infer/models/objc/src/NSData.m b/infer/models/objc/src/NSData.m index 23ae79056..8ac01d548 100644 --- a/infer/models/objc/src/NSData.m +++ b/infer/models/objc/src/NSData.m @@ -40,6 +40,13 @@ NSData* __objc_alloc(NSData*); return nil; } +- (instancetype)initWithBytesNoCopy:(void*)bytes + length:(NSUInteger)length + freeWhenDone:(BOOL)flag { + self->_bytes = bytes; + return self; +} + - (void)dealloc { if (self) free(self->_bytes); diff --git a/infer/src/clang/cField_decl.ml b/infer/src/clang/cField_decl.ml index a57a88c8b..bbd2b4d53 100644 --- a/infer/src/clang/cField_decl.ml +++ b/infer/src/clang/cField_decl.ml @@ -92,3 +92,15 @@ let add_missing_fields tenv class_name ck fields = Printing.log_out " Updating info for class '%s' in tenv\n" class_name; Tenv.add tenv class_tn_name class_type_info | _ -> () + +let modelled_fields_in_classes = [("NSData", "_bytes", Typ.Tptr (Typ.Tvoid, Typ.Pk_pointer))] + +let modelled_field class_name_info = + let modelled_field_in_class res (class_name, field_name, typ) = + if class_name = class_name_info.Clang_ast_t.ni_name then + let class_name_qualified = class_name_info.Clang_ast_t.ni_qual_name in + let field_name_qualified = Ast_utils.make_qual_name_decl class_name_qualified field_name in + let name = General_utils.mk_class_field_name field_name_qualified in + (name, typ, Typ.item_annotation_empty) :: res + else res in + IList.fold_left modelled_field_in_class [] modelled_fields_in_classes diff --git a/infer/src/clang/cField_decl.mli b/infer/src/clang/cField_decl.mli index d806ab1e1..7d8973c99 100644 --- a/infer/src/clang/cField_decl.mli +++ b/infer/src/clang/cField_decl.mli @@ -24,3 +24,5 @@ val build_sil_field : Ast_utils.type_ptr_to_sil_type -> Tenv.t -> Clang_ast_t.na Clang_ast_t.type_ptr -> Clang_ast_t.property_attribute list -> field_type val add_missing_fields : Tenv.t -> string -> Csu.class_kind -> field_type list -> unit + +val modelled_field : Clang_ast_t.named_decl_info -> field_type list diff --git a/infer/src/clang/objcInterface_decl.ml b/infer/src/clang/objcInterface_decl.ml index 550f11913..b894a04ad 100644 --- a/infer/src/clang/objcInterface_decl.ml +++ b/infer/src/clang/objcInterface_decl.ml @@ -98,7 +98,8 @@ let create_superclasses_fields type_ptr_to_sil_type tenv curr_class decl_list superclasses, fields (* Adds pairs (interface name, interface_type_info) to the global environment. *) -let add_class_to_tenv type_ptr_to_sil_type tenv curr_class decl_info class_name decl_list ocidi = +let add_class_to_tenv type_ptr_to_sil_type tenv curr_class decl_info name_info decl_list ocidi = + let class_name = Ast_utils.get_qualified_name name_info in Printing.log_out "ADDING: ObjCInterfaceDecl for '%s'\n" class_name; let interface_name = CTypes.mk_classname class_name Csu.Objc in let decl_key = `DeclPtr decl_info.Clang_ast_t.di_pointer in @@ -122,13 +123,14 @@ let add_class_to_tenv type_ptr_to_sil_type tenv curr_class decl_info class_name | _ -> fields, superclasses, methods in let fields = General_utils.append_no_duplicates_fields fields fields_sc in (* We add the special hidden counter_field for implementing reference counting *) - let fields = General_utils.append_no_duplicates_fields [Typ.objc_ref_counter_field] fields in + let modelled_fields = Typ.objc_ref_counter_field :: CField_decl.modelled_field name_info in + let all_fields = General_utils.append_no_duplicates_fields modelled_fields fields in Printing.log_out "Class %s field:\n" class_name; IList.iter (fun (fn, _, _) -> - Printing.log_out "-----> field: '%s'\n" (Ident.fieldname_to_string fn)) fields; + Printing.log_out "-----> field: '%s'\n" (Ident.fieldname_to_string fn)) all_fields; let interface_type_info = { - Typ.instance_fields = fields; + Typ.instance_fields = all_fields; static_fields = []; csu = Csu.Class Csu.Objc; struct_name = Some (Mangled.from_string class_name); @@ -170,8 +172,8 @@ let interface_declaration type_ptr_to_sil_type tenv decl = | ObjCInterfaceDecl (decl_info, name_info, decl_list, _, ocidi) -> let name = Ast_utils.get_qualified_name name_info in let curr_class = get_curr_class name ocidi in - let typ = - add_class_to_tenv type_ptr_to_sil_type tenv curr_class decl_info name decl_list ocidi in + let typ = add_class_to_tenv type_ptr_to_sil_type tenv curr_class decl_info name_info + decl_list ocidi in let _ = add_class_implementation type_ptr_to_sil_type tenv ocidi in let _ = add_super_class_decl type_ptr_to_sil_type tenv ocidi in let _ = add_protocols_decl type_ptr_to_sil_type tenv ocidi.Clang_ast_t.otdi_protocols in diff --git a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/NSStringInitWithBytesNoCopyExample.m b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/NSStringInitWithBytesNoCopyExample.m index 19e232555..a9cd5ef10 100644 --- a/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/NSStringInitWithBytesNoCopyExample.m +++ b/infer/tests/codetoanalyze/objc/errors/memory_leaks_benchmark/NSStringInitWithBytesNoCopyExample.m @@ -16,17 +16,18 @@ @implementation A -NSString* FBCreateURLQueryStringBodyEscaping(NSDictionary* parameters, - NSString* s) { +NSString* createURLQueryStringBodyEscaping(NSDictionary* parameters, + NSString* s) { + NSString* resultString; if (s) { char* resultBuffer = (char*)malloc(5 * sizeof(char)); - NSString* resultString = [s initWithBytesNoCopy:resultBuffer - length:5 - encoding:NSUTF8StringEncoding - freeWhenDone:YES]; + resultString = [s initWithBytesNoCopy:resultBuffer + length:5 + encoding:NSUTF8StringEncoding + freeWhenDone:YES]; } - return s; + return resultString; } + (NSData*)randomBytes:(NSUInteger)numOfBytes { @@ -40,6 +41,15 @@ NSString* FBCreateURLQueryStringBodyEscaping(NSDictionary* parameters, } } +- (NSData*)readDataOfLength:(NSUInteger)length { + size_t bytesLength = length; + void* bytes = malloc(bytesLength); + if (bytes == NULL) { + return nil; + } + return [[NSData alloc] initWithBytesNoCopy:bytes length:5 freeWhenDone:YES]; +} + - (NSData*)macForIV:(NSData*)IV { uint8_t* result = malloc(10); return [NSData dataWithBytesNoCopy:result length:10]; diff --git a/infer/tests/endtoend/objc/infer/MemoryLeaksFromModelsTest.java b/infer/tests/endtoend/objc/infer/MemoryLeaksFromModelsTest.java index d01db8915..afec646e9 100644 --- a/infer/tests/endtoend/objc/infer/MemoryLeaksFromModelsTest.java +++ b/infer/tests/endtoend/objc/infer/MemoryLeaksFromModelsTest.java @@ -10,8 +10,7 @@ package endtoend.objc.infer; import static org.hamcrest.MatcherAssert.assertThat; -import static utils.matchers.ResultContainsErrorInMethod.contains; -import static utils.matchers.ResultContainsNoErrorInMethod.doesNotContain; +import static utils.matchers.ResultContainsExactly.containsExactly; import com.google.common.collect.ImmutableList; @@ -50,57 +49,18 @@ public class MemoryLeaksFromModelsTest { } @Test - public void whenInferRunsOnFBCreateURLQueryStringBodyEscapingThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "FBCreateURLQueryStringBodyEscaping")); - } - - @Test - public void whenInferRunsOnRandomBytesThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "randomBytes:")); - } - - @Test - public void whenInferRunsOn_macForIVThenMLIsFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should contain memory leak", - inferResults, - contains( - MEMORY_LEAK, - memory_leak_file, - "macForIV:" - ) - ); - } - - @Test - public void whenInferRunsOnHexStringValueThenMLIsNotFound() - throws InterruptedException, IOException, InferException { - InferResults inferResults = InferRunner.runInferObjC(inferCmd); - assertThat( - "Results should not contain memory leak", - inferResults, - doesNotContain( - MEMORY_LEAK, - memory_leak_file, - "hexStringValue")); - } - +public void matchErrors() + throws InterruptedException, IOException, InferException { + InferResults inferResults = InferRunner.runInferObjC(inferCmd); + String[] procedures = {"macForIV:"}; + assertThat( + "Results should contain the expected memory leak", + inferResults, + containsExactly( + MEMORY_LEAK, + memory_leak_file, + procedures + ) + ); +} }