peci: dev: fix peci_dev use after free across adapter detach

peci_dev_detach_adapter() kfree()d the peci_dev as soon as the adapter
went away, even when userspace still had /dev/peci-N open. Later ioctls
read file->private_data->adapter out of freed memory, and the final
close did the same in peci_dev_release(). The cdev is embedded in
peci_dev too, and __fput() calls cdev_put() on it after ->release(), so
even a refcount on peci_dev alone would not be enough: the cdev
kobject would still outlive the memory it lives in.

Give peci_dev an embedded struct device and let that device own its
lifetime:

- Register it with cdev_device_add(), which makes the cdev hold a
  reference on the device. Open files pin the cdev until after
  ->release(), so peci_dev now lives until the last close.
- Free peci_dev only from the device release callback.
- Make peci_dev hold a reference on its adapter for its whole life, so
  peci_dev->adapter is always valid.
- On detach, unlink the peci_dev, cdev_device_del() it and drop the
  initial reference instead of freeing it.
- In open, find the peci_dev from inode->i_cdev rather than looking it
  up by minor, and reject the open if the bus number has already been
  reused by a different adapter.
- In name_show(), use container_of() instead of a lookup that could
  return -ENODEV while the device is still being torn down.
- Opening /dev/peci-N pins THIS_MODULE via cdev fops, preventing module
  unload while any character device file is open. To guard against
  module unload while any external or sysfs references are held, track
  active devices with a completion and wait for all release callbacks to
  complete in peci_dev_exit().

Any ioctl on a file that stays open after detach now fails with -ENODEV,
because peci_del_adapter() fences the adapter before the
BUS_NOTIFY_DEL_DEVICE notifier runs.

This replaces "peci: dev: Fix use-after-free on adapter detach", which
meta-gbmc-staging used to carry. That patch added a kref and a detached
flag but still freed the embedded cdev on the last close, before
__fput()'s cdev_put().

Google-Bug-Id: 540863271
Change-Id: I6ac2b0e705b3aba11e7cec34db1a204b96f73ad4
Signed-off-by: William A. Kennington III <wak@google.com>
1 file changed