svn commit: r341850 - in stable/11/sys/compat/linuxkpi/common: include/linux src
Hans Petter Selasky
hselasky at FreeBSD.org
Wed Dec 12 10:09:24 UTC 2018
Author: hselasky
Date: Wed Dec 12 10:09:23 2018
New Revision: 341850
URL: https://svnweb.freebsd.org/changeset/base/341850
Log:
MFC r341518:
linuxkpi: Fix for use-after-free when tearing down character devices.
Make sure we hold a reference on the character device for every opened file
to prevent the character device to be freed prematurely.
Sponsored by: Mellanox Technologies
Modified:
stable/11/sys/compat/linuxkpi/common/include/linux/cdev.h
stable/11/sys/compat/linuxkpi/common/include/linux/fs.h
stable/11/sys/compat/linuxkpi/common/src/linux_compat.c
Directory Properties:
stable/11/ (props changed)
Modified: stable/11/sys/compat/linuxkpi/common/include/linux/cdev.h
==============================================================================
--- stable/11/sys/compat/linuxkpi/common/include/linux/cdev.h Wed Dec 12 10:08:15 2018 (r341849)
+++ stable/11/sys/compat/linuxkpi/common/include/linux/cdev.h Wed Dec 12 10:09:23 2018 (r341850)
@@ -36,6 +36,8 @@
#include <linux/kdev_t.h>
#include <linux/list.h>
+#include <asm/atomic-long.h>
+
struct file_operations;
struct inode;
struct module;
@@ -50,6 +52,7 @@ struct linux_cdev {
struct cdev *cdev;
dev_t dev;
const struct file_operations *ops;
+ atomic_long_t refs;
};
static inline void
@@ -58,6 +61,7 @@ cdev_init(struct linux_cdev *cdev, const struct file_o
kobject_init(&cdev->kobj, &linux_cdev_static_ktype);
cdev->ops = ops;
+ atomic_long_set(&cdev->refs, 0);
}
static inline struct linux_cdev *
@@ -130,13 +134,13 @@ cdev_add_ext(struct linux_cdev *cdev, dev_t dev, uid_t
return (0);
}
+void linux_destroy_dev(struct linux_cdev *);
+
static inline void
cdev_del(struct linux_cdev *cdev)
{
- if (cdev->cdev) {
- destroy_dev(cdev->cdev);
- cdev->cdev = NULL;
- }
+
+ linux_destroy_dev(cdev);
kobject_put(&cdev->kobj);
}
Modified: stable/11/sys/compat/linuxkpi/common/include/linux/fs.h
==============================================================================
--- stable/11/sys/compat/linuxkpi/common/include/linux/fs.h Wed Dec 12 10:08:15 2018 (r341849)
+++ stable/11/sys/compat/linuxkpi/common/include/linux/fs.h Wed Dec 12 10:09:23 2018 (r341850)
@@ -2,7 +2,7 @@
* Copyright (c) 2010 Isilon Systems, Inc.
* Copyright (c) 2010 iX Systems, Inc.
* Copyright (c) 2010 Panasas, Inc.
- * Copyright (c) 2013-2017 Mellanox Technologies, Ltd.
+ * Copyright (c) 2013-2018 Mellanox Technologies, Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -55,6 +55,7 @@ struct vm_area_struct;
struct poll_table_struct;
struct files_struct;
struct pfs_node;
+struct linux_cdev;
#define inode vnode
#define i_cdev v_rdev
@@ -105,6 +106,9 @@ struct linux_file {
/* protects f_selinfo.si_note */
spinlock_t f_kqlock;
struct linux_file_wait_queue f_wait_queue;
+
+ /* pointer to associated character device, if any */
+ struct linux_cdev *f_cdev;
};
#define file linux_file
Modified: stable/11/sys/compat/linuxkpi/common/src/linux_compat.c
==============================================================================
--- stable/11/sys/compat/linuxkpi/common/src/linux_compat.c Wed Dec 12 10:08:15 2018 (r341849)
+++ stable/11/sys/compat/linuxkpi/common/src/linux_compat.c Wed Dec 12 10:09:23 2018 (r341850)
@@ -699,12 +699,20 @@ linux_dev_fdopen(struct cdev *dev, int fflags, struct
filp->f_flags = file->f_flag;
filp->f_vnode = file->f_vnode;
filp->_file = file;
+ filp->f_cdev = ldev;
linux_set_current(td);
+ /* get a reference on the Linux character device */
+ if (atomic_long_add_unless(&ldev->refs, 1, -1L) == 0) {
+ kfree(filp);
+ return (EINVAL);
+ }
+
if (filp->f_op->open) {
error = -filp->f_op->open(file->f_vnode, filp);
if (error) {
+ atomic_long_dec(&ldev->refs);
kfree(filp);
return (error);
}
@@ -1396,6 +1404,10 @@ linux_file_close(struct file *file, struct thread *td)
funsetown(&filp->f_sigio);
if (filp->f_vnode != NULL)
vdrop(filp->f_vnode);
+ if (filp->f_cdev != NULL) {
+ /* put a reference on the Linux character device */
+ atomic_long_dec(&filp->f_cdev->refs);
+ }
kfree(filp);
return (error);
@@ -1947,8 +1959,7 @@ linux_cdev_release(struct kobject *kobj)
cdev = container_of(kobj, struct linux_cdev, kobj);
parent = kobj->parent;
- if (cdev->cdev)
- destroy_dev(cdev->cdev);
+ linux_destroy_dev(cdev);
kfree(cdev);
kobject_put(parent);
}
@@ -1961,9 +1972,25 @@ linux_cdev_static_release(struct kobject *kobj)
cdev = container_of(kobj, struct linux_cdev, kobj);
parent = kobj->parent;
- if (cdev->cdev)
- destroy_dev(cdev->cdev);
+ linux_destroy_dev(cdev);
kobject_put(parent);
+}
+
+void
+linux_destroy_dev(struct linux_cdev *cdev)
+{
+
+ if (cdev->cdev == NULL)
+ return;
+
+ atomic_long_dec(&cdev->refs);
+
+ /* wait for all open files to be closed */
+ while (atomic_long_read(&cdev->refs) != -1L)
+ pause("ldevdrn", hz);
+
+ destroy_dev(cdev->cdev);
+ cdev->cdev = NULL;
}
const struct kobj_type linux_cdev_ktype = {
More information about the svn-src-stable-11
mailing list