瀏覽代碼

Fix potential concurrent problems in chardev2.c

After forking, Each file descriptor in the child refers to the same
open file description as the parent. So when calling open() before
fork(), the child can access the device file without checking by
exclusive access in device_open(). It may cause race conditions
in device_ioctl().

Because of that, it is unnecessary to check the multiple access
in device_open(). It just needs check in device_ioctl(), since
read(), write(), seek() system call are atomic [1][2].

Related discussion:
- https://github.com/sysprog21/lkmpg/issues/148

[1] https://lore.kernel.org/lkml/53022DB1.4070805@gmail.com/
[2] https://www.kernel.org/doc/html/latest/filesystems/files.html

Close #148
linD026 3 年之前
父節點
當前提交
beb1ff1595
共有 1 個文件被更改,包括 10 次插入9 次删除
  1. 10 9
      examples/chardev2.c

+ 10 - 9
examples/chardev2.c

@@ -37,10 +37,6 @@ static int device_open(struct inode *inode, struct file *file)
 {
     pr_info("device_open(%p)\n", file);
 
-    /* We don't want to talk to two processes at the same time. */
-    if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN))
-        return -EBUSY;
-
     try_module_get(THIS_MODULE);
     return SUCCESS;
 }
@@ -49,9 +45,6 @@ static int device_release(struct inode *inode, struct file *file)
 {
     pr_info("device_release(%p,%p)\n", inode, file);
 
-    /* We're now ready for our next caller */
-    atomic_set(&already_open, CDEV_NOT_USED);
-
     module_put(THIS_MODULE);
     return SUCCESS;
 }
@@ -129,6 +122,11 @@ device_ioctl(struct file *file, /* ditto */
              unsigned long ioctl_param)
 {
     int i;
+    long ret = SUCCESS;
+
+    /* We don't want to talk to two processes at the same time. */
+    if (atomic_cmpxchg(&already_open, CDEV_NOT_USED, CDEV_EXCLUSIVE_OPEN))
+        return -EBUSY;
 
     /* Switch according to the ioctl called */
     switch (ioctl_num) {
@@ -166,11 +164,14 @@ device_ioctl(struct file *file, /* ditto */
         /* This ioctl is both input (ioctl_param) and output (the return
          * value of this function).
          */
-        return (long)message[ioctl_param];
+        ret = (long)message[ioctl_param];
         break;
     }
 
-    return SUCCESS;
+    /* We're now ready for our next caller */
+    atomic_set(&already_open, CDEV_NOT_USED);
+
+    return ret;
 }
 
 /* Module Declarations */