Source-Changes-D archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: CVS commit: src/sys/kern



Andrew Doran wrote:
On Mon, Apr 20, 2009 at 10:09:55PM +0000, Elad Efrat wrote:

Module Name:    src
Committed By:   elad
Date:           Mon Apr 20 22:09:55 UTC 2009

Modified Files:
        src/sys/kern: kern_verifiedexec.c

Log Message:
PR/41251: YAMAMOTO Takashi: veriexec locking seems broken

Part 1: Take the mountlist_lock before traversing the mount list.

Thanks for looking into this. However, there are some problems with your
change:

- mountlist_lock is a `leaf'. That is to say, it is never held for long and
  is never held for heavyweight operations. It also doesn't get intermixed
  with other locks.

- For similar reasons, I suspect your change may introduce a lock ordering
  between mountlist_lock and other locks, which is undesirable.

- The mountpoints you are looking at can be unmounted while you're examining
  them. The 'struct mount' won't disappear since you hold mountlist_lock,
  but see above.

The solution to this is vfs_busy+vfs_unbusy. Have a grep in kern/ for uses
of mountlist_lock. I think there are a few loops where all three are used
together. Basically, they give you this:

- vfs_busy() drops mountlist lock, but holds a reference to the mount for
  you, so it will not go away while you are inspecting it.

- vfs_unbusy() reacquires mountlist_lock, and lets you continue traversing
  the list from where you left off. It also drpos the reference.

- vfs_busy() locks the mount to prevent unmount().


Thanks for pointing these out! I did as you suggested -- please take a
look at the attached diff.

While looking around, a few questions came up:

  - Take a look at:

        http://nxr.netbsd.org/source/xref/sys/kern/vfs_subr.c#2120

    It seems that if vnalloc() fails, we're not releasing the
    mountlist_mutex and not doing vfs_unbusy() -- is this a problem?
    (this holds for the other error paths in this function too)

  - It seems that the two functions that use CIRCLEQ_FOREACH() to
    traverse the mountlist don't vfs_busy()/vfs_unbusy(). Is this okay
    because they're supposed to be "light" users? (if yes, where do we
    draw the line between light/heavy uses? :)

  - Should I also convert to an "open coded" loop, or is the diff
    attached okay?

  - I'm not sure how much of a problem it is, but since we're dropping
    the mountlist lock in the middle, doesn't it mean it's possible to
    insert elements into the mountlist "before" elements we've already
    passed? (in other words, perhaps we should make the mountlist KPI
    only append to the list, rather than exposing it as a CIRCLEQ?)

Thanks,

-e.
Index: kern_verifiedexec.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_verifiedexec.c,v
retrieving revision 1.113
diff -u -p -r1.113 kern_verifiedexec.c
--- kern_verifiedexec.c 20 Apr 2009 22:09:54 -0000      1.113
+++ kern_verifiedexec.c 22 Apr 2009 21:10:59 -0000
@@ -1539,8 +1539,16 @@ veriexec_dump(struct lwp *l, prop_array_
 
        mutex_enter(&mountlist_lock);
        CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) {
+               struct mount *nmp;
+
+               /* If it fails, the file-system is [being] unmounted. */
+               if (vfs_busy(mp, &mnp) != 0)
+                       continue;
+
                fileassoc_table_run(mp, veriexec_hook,
                    (fileassoc_cb_t)veriexec_file_dump, rarray);
+
+               vfs_unbusy(mp, false, &nmp);
        }
        mutex_exit(&mountlist_lock);
 
@@ -1555,11 +1563,18 @@ veriexec_flush(struct lwp *l)
 
        mutex_enter(&mountlist_lock);
        CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) {
+               struct mount *nmp;
                int lerror;
 
+               /* If it fails, the file-system is [being] unmounted. */
+               if (vfs_busy(mp, &nmp) != 0)
+                       continue;
+
                lerror = veriexec_table_delete(l, mp);
                if (lerror && lerror != ENOENT)
                        error = lerror;
+
+               vfs_unbusy(mp, false, &nmp);
        }
        mutex_exit(&mountlist_lock);
 


Home | Main Index | Thread Index | Old Index