elan: Fix use-after-free if USB transfer is cancelled
This broke deactivation. It used to go like
fpi_ssm_mark_failed(ssm, -ECANCELED) -> fpi_ssm_free(ssm) -> <return> elan_deactivate(dev) -> fpi_imgdev_deactivate_complete(dev) -> <free elandev etc.> -> fpi_drvcb_close_complete()
Now transfer cancellation leaves the device in unresponsive state after
$ fprintd-enroll Using device /net/reactivated/Fprint/Device/0 Enrolling right-index-finger finger. ^C $ fprintd-enroll Using device /net/reactivated/Fprint/Device/0 failed to claim device: Device was already claimed
It must be power-cycled before it can be used again. What exactly was used after free? Maybe I could add some checks to work around it because deactivation needs to finish properly.
I think the idea here is that the transfer callback may be called after the device is closed already. And in that case the pointers are invalid.
It is a convention in GLib/GObject style async handling to always first check for cancellation and then only access any data if the operation was not cancelled. This is because in the usual case the reason for the cancellation is or at least may be that the object owning the operation is being destroyed.
In libfprint what usually seems to happen though is that the
deactivatecall cancels a USB transfer. And then deactivation succeeds once the state machine finishes (in this case by marking it as failed). So my guess–without having looked more closely–is that the old code was correct.