aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Spelvin <linux@horizon.com>2013-02-12 02:27:20 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-02-13 12:40:35 -0500
commitd953e0e837e65ecc1ddaa4f9560f7925878a0de6 (patch)
tree64728d65488a9f85836f570640b1fec863737a52
parent03a7ffe4e542310838bac70ef85acc17536b6d7c (diff)
pps: Fix a use-after free bug when unregistering a source.
Remove the cdev from the system (with cdev_del) *before* deallocating it (in pps_device_destruct, called via kobject_put from device_destroy). Also prevent deallocating a device with open file handles. A better long-term fix is probably to remove the cdev from the pps_device entirely, and instead have all devices reference one global cdev. Then the deallocation ordering becomes simpler. But that's more complex and invasive change, so we leave that for later. Signed-off-by: George Spelvin <linux@horizon.com> Cc: stable <stable@vger.kernel.org> Acked-by: Rodolfo Giometti <giometti@enneenne.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/pps/pps.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index a70e384262e5..6437703eb10f 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -247,12 +247,15 @@ static int pps_cdev_open(struct inode *inode, struct file *file)
247 struct pps_device *pps = container_of(inode->i_cdev, 247 struct pps_device *pps = container_of(inode->i_cdev,
248 struct pps_device, cdev); 248 struct pps_device, cdev);
249 file->private_data = pps; 249 file->private_data = pps;
250 250 kobject_get(&pps->dev->kobj);
251 return 0; 251 return 0;
252} 252}
253 253
254static int pps_cdev_release(struct inode *inode, struct file *file) 254static int pps_cdev_release(struct inode *inode, struct file *file)
255{ 255{
256 struct pps_device *pps = container_of(inode->i_cdev,
257 struct pps_device, cdev);
258 kobject_put(&pps->dev->kobj);
256 return 0; 259 return 0;
257} 260}
258 261
@@ -274,8 +277,10 @@ static void pps_device_destruct(struct device *dev)
274{ 277{
275 struct pps_device *pps = dev_get_drvdata(dev); 278 struct pps_device *pps = dev_get_drvdata(dev);
276 279
277 /* release id here to protect others from using it while it's 280 cdev_del(&pps->cdev);
278 * still in use */ 281
282 /* Now we can release the ID for re-use */
283 pr_debug("deallocating pps%d\n", pps->id);
279 mutex_lock(&pps_idr_lock); 284 mutex_lock(&pps_idr_lock);
280 idr_remove(&pps_idr, pps->id); 285 idr_remove(&pps_idr, pps->id);
281 mutex_unlock(&pps_idr_lock); 286 mutex_unlock(&pps_idr_lock);
@@ -332,6 +337,7 @@ int pps_register_cdev(struct pps_device *pps)
332 goto del_cdev; 337 goto del_cdev;
333 } 338 }
334 339
340 /* Override the release function with our own */
335 pps->dev->release = pps_device_destruct; 341 pps->dev->release = pps_device_destruct;
336 342
337 pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, 343 pr_debug("source %s got cdev (%d:%d)\n", pps->info.name,
@@ -352,9 +358,9 @@ free_idr:
352 358
353void pps_unregister_cdev(struct pps_device *pps) 359void pps_unregister_cdev(struct pps_device *pps)
354{ 360{
361 pr_debug("unregistering pps%d\n", pps->id);
355 pps->lookup_cookie = NULL; 362 pps->lookup_cookie = NULL;
356 device_destroy(pps_class, pps->dev->devt); 363 device_destroy(pps_class, pps->dev->devt);
357 cdev_del(&pps->cdev);
358} 364}
359 365
360/* 366/*