infer icon indicating copy to clipboard operation
infer copied to clipboard

[Biabduction] [Objective-C] Retain Cycle not work when wrapping cycles.

Open skyleaworlder opened this issue 2 years ago • 0 comments

Please include the following information:

  • [x] Infer version: v1.1.0.
  • [x] Your operating system: "MacOS-12.1".
  • [x] The command I ran: infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main.m -o main
  • [x] The full output see below.
  • [x] A minimal example to reproduce your problem, see below.

1. Only Objective-C code

@JumpMasterJJ and I try to use infer biabduction to analyze Objective-C "Retain Cycle". This is our code:

#import <Foundation/Foundation.h>
@class Child;
@class Parent;
struct ParentWrapper {
    Parent *parent;
};
struct ChildWrapper {
    Child *child;
};
@interface Parent : NSObject {
    @public struct ChildWrapper childWrapper;
}
-(void) dealloc;
@end
@interface Child : NSObject {
    @public struct ParentWrapper parentWrapper;
}
-(void) dealloc;
@end
@implementation Parent
-(void) dealloc {
    NSLog(@"this is parent dealloc function");
}
@end
@implementation Child
-(void) dealloc {
    NSLog(@"this is parent dealloc function");
}
@end
int foo(int argc, const char* argv[]) {
    Parent *parent = [[Parent alloc] init];
    Child *child = [[Child alloc] init];
    parent->childWrapper.child = child;
    child->parentWrapper.parent = parent;
    return 0;
}
int main(int argc, const char* argv[]) {
    foo(argc, argv);
    return 0;
}

You can easily know there is a reference counting problem in the above code. And I can confirm it since I also compiled it without infer. It didn't print log due to retain cycles:

clang -fobjc-arc -framework Foundation main.m -o main

./main
# No output

If I remove child->parentWrapper.parent = parent;, it can print logs successfully:

#import <Foundation/Foundation.h>
@class Child;
@class Parent;
struct ParentWrapper {
    Parent *parent;
};
struct ChildWrapper {
    Child *child;
};
@interface Parent : NSObject {
    @public struct ChildWrapper childWrapper;
}
-(void) dealloc;
@end
@interface Child : NSObject {
    @public struct ParentWrapper parentWrapper;
}
-(void) dealloc;
@end
@implementation Parent
-(void) dealloc {
    NSLog(@"this is parent dealloc function");
}
@end
@implementation Child
-(void) dealloc {
    NSLog(@"this is parent dealloc function");
}
@end
int foo(int argc, const char* argv[]) {
    Parent *parent = [[Parent alloc] init];
    Child *child = [[Child alloc] init];
    parent->childWrapper.child = child;
    // child->parentWrapper.parent = parent;
    return 0;
}
int main(int argc, const char* argv[]) {
    foo(argc, argv);
    return 0;
}

It can print:

./main
2023-05-17 19:32:49.180 main[25913:168343] this is parent dealloc function
2023-05-17 19:32:49.180 main[25913:168343] this is parent dealloc function

2. With infer biabduction

We ran for the first piece of code:

infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main.m -o main

But I got:

1/1 [################################################################################] 100% 514ms

No issues found

3. Potential problem

We also try to write some other pieces of Objective-C program, like this:

#import <Foundation/Foundation.h>

@class Child;
@interface Parent : NSObject {
    @public Child *child;
}
@end

@interface Child : NSObject {
    @public Parent *parent;
}
@end

@implementation Parent
@end

@implementation Child
@end

int foo(int argc, const char* argv[]) {
    Parent *parent = [[Parent alloc] init];
    Child *child = [[Child alloc] init];

    parent->child = child;
    child->parent = parent;
    return 0;
}

int main(int argc, const char* argv[]) {
    return 0;
}

Apparently, it also has the same kind of problem. And we ran infer:

infer run --biabduction --debug -- clang -fobjc-arc -framework Foundation main-simple.m -o main-simple

1/1 [################################################################################] 100% 800ms

main-simple.m:25: error: Biabduction Retain Cycle
  Retain cycle at line 25, column 5 involving the following objects:
 (1) `child` of type `Child*` --> `parent`, last assigned on line 25
 (2) `parent` of type `Parent*` --> `child`, last assigned on line 24.
  23. 
  24.     parent->child = child;
  25.     child->parent = parent;
          ^
  26.     return 0;
  27. }


Found 1 issue
                          Issue Type(ISSUED_TYPE_ID): #
  Biabduction Retain Cycle(BIABDUCTION_RETAIN_CYCLE): 1

So we think that it's related to "wrapper", which means that current Retain Cycle checker might be not able to handle nested data structure.

skyleaworlder avatar May 17 '23 11:05 skyleaworlder