Message ID | 1484356230.2527.76.camel@HansenPartnership.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Jan 13, 2017 at 05:10:30PM -0800, James Bottomley wrote: > > No, it is correct as is. The cdev fops rely only on the tpm module. > > When tpm_chip_unregister returns to the driver the chips->ops is set > > to NULL with proper locking - the driver code becomes uncallable at > > that point. > > So that's the nastily subtle point I was missing. The patch below will > add the reference tracking to make sure the chip is held while the > /dev/tpms<n> is open. I really hate the additional layer of subtlety > this adds, though ... I've tried to document the extra nasties, but I > bet they confuse someone. Thanks > Doing it the standard way, where the owner is set to the pdev driver > module and the references all track up to the pdev, meaning the actual > device could never go away while a cdev file was open would be far > easier to understand. For a long time now devices can be unbound without a module unload. Eg the 'unbind' file in sysfs. Subsystems can no longer rely on module locking to block unbind. vtpm makes use of hot-unplug when the server side is closed. We audited this area extensively when vtpm was introduced to make it work properly. Not all subsystems are correct in this regard, and you may still find vestigial assumptions that module locking is enough to fix locking/refcounting sins. It is not, those places are just broken. :( Jason -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 07d7607..338592c 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -132,6 +132,14 @@ static void tpm_dev_release(struct device *dev) kfree(chip); } +static void tpm_devrm_release(struct device *dev) +{ + struct tpm_chip *chip = container_of(dev, struct tpm_chip, devrm); + + /* release the master device reference */ + put_device(&chip->dev); +} + /** * tpm_chip_alloc() - allocate a new struct tpm_chip instance * @pdev: device to which the chip is associated @@ -178,6 +186,12 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, chip->devrm.parent = pdev; chip->devrm.class = tpm_rm_class; + chip->devrm.release = tpm_devrm_release; + /* get extra reference on main device to hold on + * behalf of devrm. This holds the chip structure + * while cdevrm is in use. The corresponding put + * is in the tpm_devrm_release */ + get_device(&chip->dev); if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); @@ -207,6 +221,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev, return chip; out: + put_device(&chip->devrm); put_device(&chip->dev); return ERR_PTR(rc); } @@ -323,6 +338,9 @@ static void tpm_del_char_device(struct tpm_chip *chip) tpm2_shutdown(chip, TPM2_SU_CLEAR); chip->ops = NULL; up_write(&chip->ops_sem); + /* will release the devrm reference to the chip->dev unless + * something has cdevrm open */ + put_device(&chip->devrm); } static void tpm_del_legacy_sysfs(struct tpm_chip *chip)