Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IBOutlet crashing with EXC_BAD_ACCESS even though not nil

In a UIViewController (rolePageController) I configure another UIViewController (drawerController) and pass it 2 UIViews from the role page that will be part of the drawerController's configuration. As soon as the drawerController tries to access the IBOutlet views from the rolePageController, it crashes with EXC_BAD_ACCESS (code=EXC_I386_GPFLT).

In the 1st VC (rolePageController), here are the IBOutlets:

@IBOutlet var rolePageDrawerView: UIView!
@IBOutlet var rolePageContentView: UIView!

In rolePageController.viewDidLoad() I make a call to the drawerController.configureDrawer(...):

override func viewDidLoad() {
    super.viewDidLoad()

    //other stuff happens here

    let drawerController = UIStoryboard(name: "StoryboardName", bundle: nil).instantiateViewController(withIdentifier: "drawerController") as! DrawerViewController
    drawerController.configureDrawer(drawerContainerView: self.rolePageDrawerView, overlaidView: self.rolePageContentView)

    //other stuff here
}

The DrawerViewController protocol is defined as:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

Here is the code for the configureDrawer(...) func:

private var drawerParentView: UIView!
private var overlaidByDrawerView: UIView!


func configureDrawer(drawerContainerView: UIView, overlaidView: UIView) {
    self.drawerParentView = drawerContainerView
    self.overlaidByDrawerView = overlaidView
}

Noticed in the debugger that the drawerController instance that is called does not match the self instance that receives the call. Here is the address of the instance that will be called:

enter image description here

Here is the address of the instance when I step into the call:

enter image description here

The address of drawerController before the call is not the address of self when I step into the call. That should never happen.

I have created a simplified project that reproduces the crash at https://github.com/ksoftllc/DynamicStackBufferOverflow.

Solution Solution turned out to be to remove the where clause from the DrawerViewController protocol.

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}
like image 227
Chuck Krutsinger Avatar asked Dec 07 '18 02:12

Chuck Krutsinger


2 Answers

Found the offending code, but I don't know why this would cause the errors I was seeing. The drawerController conforms to DrawerViewController protocol, defined as:

protocol DrawerViewController where Self: UIViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

When I remove the Where condition, it no longer crashes.

protocol DrawerViewController {
    func configureDrawer(drawerContainerView: UIView, overlaidView: UIView)
}

The where clause was not actually necessary for correct function of the program, so I will proceed without it.

UPDATE I filed a bug with swift.org and received a response. Adding a where clause to a protocol is not supported in Swift 4.2, but will be supported in Swift 5.0. In addition, @J Doe posted below a way to accomplish this with an update to Xcode toolkit.

like image 164
Chuck Krutsinger Avatar answered Nov 15 '22 17:11

Chuck Krutsinger


dynamic-stack-buffer-overflow doesn't have anything to do with recursion. It means an alloca buffer was overrun. Check the asan runtime source code.

Suppose the stack is laid out so that you have an alloca buffer followed by an object pointer—maybe even one of the object pointers passed as an argument.

Suppose the alloca buffer gets overrun. In an asan build, this can trigger a dynamic-stack-buffer-overflow error. But in a non-asan build, it just writes over the bytes of that object pointer. Suppose it writes bytes that form an address that's not mapped in your process's page table.

If the program tries to read that object pointer and store it elsewhere (say, in an instance variable), it has to increment the reference count of the object. But that means dereferencing the pointer—and the pointer points to an unmapped address. Perhaps that's leading to a general protection fault, which Mach calls an EXC_I386_GPFLT.

It would be helpful if you posted the stack trace of the asan dynamic-stack-buffer-overflow error, and the disassembly of the code leading up to the error.

like image 44
rob mayoff Avatar answered Nov 15 '22 18:11

rob mayoff