kernel: Allow fhold() in allfiles_scan_exclusive()
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Wed, 23 Sep 2015 12:26:38 +0000 (20:26 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Sat, 26 Sep 2015 02:15:04 +0000 (10:15 +0800)
commitd0505a2a9878750f03aa91af3f422e09b685d5e9
treefbafd80c51ec8b711a0e2bb7794a196676e446b2
parent84365e818711acf89946aa23e2a308548b89da76
kernel: Allow fhold() in allfiles_scan_exclusive()

Before this commit following race could happen, if fhold() is used
during allfiles_scan_exclusive():

      Thread1                      Thread2
         :                            :
         :                      fdrop(fp)
         :                      {
    Scan all fps                  if (f_count-- == 1) {
    (*) fhold(fp) [f_count++]         :
         :                          fo_close(fp);
    Access fp                       Unlink fp from filehead;
    fdrop(fp)                       free(fp);
         :                        }
         :                      }

This could cause use-after-free or double free (since f_count == 1
when fdrop() was called in Thread1, fp will be freed again).

We now handle f_count 1->0 transition specially: hold the filehead
spin lock then do the f_count 1->0 transition, if the f_count 1->0
transition succeeds, the fp is unlinked from the filehead.  This
prevents the above race from happening.  This solution is mainly
based on dillon@'s input.
sys/kern/kern_descrip.c