Re: [patch] prevent slab corruption by fixing race codition in cifs

Shirish Pargaonkar <[email protected]>
Newsgroups gmane.linux.file-systems.cifs
Message-ID <[email protected]>
The testing, so far looks good, the same tests that detected this slab
memory corruption
have run without any errors/corruption for more than 24 hours.

On Tue, Aug 25, 2009 at 4:23 PM, Dave Kleikamp<[email protected]> wrote:
> On Tue, 2009-08-18 at 12:43 -0400, Jeff Layton wrote:
>> On Tue, 18 Aug 2009 10:23:09 -0500
>> Shirish Pargaonkar <[email protected]> wrote:
>>
>> > On Tue, Aug 18, 2009 at 6:15 AM, Jeff Layton<[email protected]> wrote:
>
>> > > I'm less than thrilled with this patch. This looks like like it's
>> > > layering more complexity onto a codepath that is already far too
>> > > complex for what it does.
>> > >
>> > > Is it not possible to just use regular old refcounting for the open
>> > > filehandles? i.e. have each user of the filehandle take a reference,
>> > > and the last one to put it does the actual close. That seems like a
>> > > much better approach to me than all of this crazy flag business.
>> > >
>> >
>> > I think this needs, some re-designing the code right?
>> >
>>
>> My vote would be "yes". Redesign and simplify the code rather than
>> adding in new hacks to work around the flaws in the existing design.
>>
>> Redesigning it with actual refcounting (and not this half-assed
>> wrtPending stuff) seems like a much better approach.
>
> How's this?  Untested so far:
>
> cifs: Replace wrtPending with a real reference count
>
> Currently, cifs_close() tries to wait until all I/O is complete and then
> frees the file private data.  If I/O does not completely in a reasonable
> amount of time it frees the structure anyway, leaving a potential use-
> after-free situation.
>
> This patch changes the wrtPending counter to a complete reference count and
> lets the last user free the structure.
>
> WARNING: compile tested only at this time
>
> Signed-off-by: Dave Kleikamp <[email protected]>
>
> diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
> index 6941c22..7dfe084 100644
> --- a/fs/cifs/cifsacl.c
> +++ b/fs/cifs/cifsacl.c
> @@ -607,7 +607,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
>                return get_cifs_acl_by_path(cifs_sb, path, pacllen);
>
>        pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
> -       atomic_dec(&open_file->wrtPending);
> +       cifsFileInfo_put(open_file);
>        return pntsd;
>  }
>
> @@ -665,7 +665,7 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
>                return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
>
>        rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
> -       atomic_dec(&open_file->wrtPending);
> +       cifsFileInfo_put(open_file);
>        return rc;
>  }
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 6084d63..e3b1161 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -351,11 +351,24 @@ struct cifsFileInfo {
>        bool closePend:1;       /* file is marked to close */
>        bool invalidHandle:1;   /* file closed via session abend */
>        bool messageMode:1;     /* for pipes: message vs byte mode */
> -       atomic_t wrtPending;   /* handle in use - defer close */
> +       atomic_t count;         /* reference count */
>        struct mutex fh_mutex; /* prevents reopen race after dead ses*/
>        struct cifs_search_info srch_inf;
>  };
>
> +/* Take a reference on the file private data */
> +static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
> +{
> +       atomic_inc(&cifs_file->count);
> +}
> +
> +/* Release a reference on the file private data */
> +static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
> +{
> +       if (atomic_dec_and_test(&cifs_file->count))
> +               kfree(cifs_file);
> +}
> +
>  /*
>  * One of these for each file inode
>  */
> diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
> index 4326ffd..a6424cf 100644
> --- a/fs/cifs/dir.c
> +++ b/fs/cifs/dir.c
> @@ -153,7 +153,7 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
>        mutex_init(&pCifsFile->fh_mutex);
>        mutex_init(&pCifsFile->lock_mutex);
>        INIT_LIST_HEAD(&pCifsFile->llist);
> -       atomic_set(&pCifsFile->wrtPending, 0);
> +       atomic_set(&pCifsFile->count, 1);
>
>        /* set the following in open now
>                        pCifsFile->pfile = file; */
> diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> index c34b7f8..fa7beac 100644
> --- a/fs/cifs/file.c
> +++ b/fs/cifs/file.c
> @@ -53,11 +53,9 @@ static inline struct cifsFileInfo *cifs_init_private(
>        private_data->pInode = inode;
>        private_data->invalidHandle = false;
>        private_data->closePend = false;
> -       /* we have to track num writers to the inode, since writepages
> -       does not tell us which handle the write is for so there can
> -       be a close (overlapping with write) of the filehandle that
> -       cifs_writepages chose to use */
> -       atomic_set(&private_data->wrtPending, 0);
> +       /* Initialize reference count to one.  The private data is
> +       freed on the release of the last reference */
> +       atomic_set(&private_data->count, 1);
>
>        return private_data;
>  }
> @@ -643,7 +641,7 @@ int cifs_close(struct inode *inode, struct file *file)
>                        if (!pTcon->need_reconnect) {
>                                write_unlock(&GlobalSMBSeslock);
>                                timeout = 2;
> -                               while ((atomic_read(&pSMBFile->wrtPending) != 0)
> +                               while ((atomic_read(&pSMBFile->count) != 1)
>                                        && (timeout <= 2048)) {
>                                        /* Give write a better chance to get to
>                                        server ahead of the close.  We do not
> @@ -657,8 +655,6 @@ int cifs_close(struct inode *inode, struct file *file)
>                                        msleep(timeout);
>                                        timeout *= 4;
>                                }
> -                               if (atomic_read(&pSMBFile->wrtPending))
> -                                       cERROR(1, ("close with pending write"));
>                                if (!pTcon->need_reconnect &&
>                                    !pSMBFile->invalidHandle)
>                                        rc = CIFSSMBClose(xid, pTcon,
> @@ -681,24 +677,7 @@ int cifs_close(struct inode *inode, struct file *file)
>                list_del(&pSMBFile->flist);
>                list_del(&pSMBFile->tlist);
>                write_unlock(&GlobalSMBSeslock);
> -               timeout = 10;
> -               /* We waited above to give the SMBWrite a chance to issue
> -                  on the wire (so we do not get SMBWrite returning EBADF
> -                  if writepages is racing with close.  Note that writepages
> -                  does not specify a file handle, so it is possible for a file
> -                  to be opened twice, and the application close the "wrong"
> -                  file handle - in these cases we delay long enough to allow
> -                  the SMBWrite to get on the wire before the SMB Close.
> -                  We allow total wait here over 45 seconds, more than
> -                  oplock break time, and more than enough to allow any write
> -                  to complete on the server, or to time out on the client */
> -               while ((atomic_read(&pSMBFile->wrtPending) != 0)
> -                               && (timeout <= 50000)) {
> -                       cERROR(1, ("writes pending, delay free of handle"));
> -                       msleep(timeout);
> -                       timeout *= 8;
> -               }
> -               kfree(file->private_data);
> +               cifsFileInfo_put(file->private_data);
>                file->private_data = NULL;
>        } else
>                rc = -EBADF;
> @@ -1236,7 +1215,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
>                        if (!open_file->invalidHandle) {
>                                /* found a good file */
>                                /* lock it so it will not be closed on us */
> -                               atomic_inc(&open_file->wrtPending);
> +                               cifsFileInfo_get(open_file);
>                                read_unlock(&GlobalSMBSeslock);
>                                return open_file;
>                        } /* else might as well continue, and look for
> @@ -1276,7 +1255,7 @@ refind_writable:
>                if (open_file->pfile &&
>                    ((open_file->pfile->f_flags & O_RDWR) ||
>                     (open_file->pfile->f_flags & O_WRONLY))) {
> -                       atomic_inc(&open_file->wrtPending);
> +                       cifsFileInfo_get(open_file);
>
>                        if (!open_file->invalidHandle) {
>                                /* found a good writable file */
> @@ -1293,7 +1272,7 @@ refind_writable:
>                                else { /* start over in case this was deleted */
>                                       /* since the list could be modified */
>                                        read_lock(&GlobalSMBSeslock);
> -                                       atomic_dec(&open_file->wrtPending);
> +                                       cifsFileInfo_put(open_file);
>                                        goto refind_writable;
>                                }
>                        }
> @@ -1309,7 +1288,7 @@ refind_writable:
>                        read_lock(&GlobalSMBSeslock);
>                        /* can not use this handle, no write
>                           pending on this one after all */
> -                       atomic_dec(&open_file->wrtPending);
> +                       cifsFileInfo_put(open_file);
>
>                        if (open_file->closePend) /* list could have changed */
>                                goto refind_writable;
> @@ -1373,7 +1352,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
>        if (open_file) {
>                bytes_written = cifs_write(open_file->pfile, write_data,
>                                           to-from, &offset);
> -               atomic_dec(&open_file->wrtPending);
> +               cifsFileInfo_put(open_file);
>                /* Does mm or vfs already set times? */
>                inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
>                if ((bytes_written > 0) && (offset))
> @@ -1562,7 +1541,7 @@ retry:
>                                                   bytes_to_write, offset,
>                                                   &bytes_written, iov, n_iov,
>                                                   long_op);
> -                               atomic_dec(&open_file->wrtPending);
> +                               cifsFileInfo_put(open_file);
>                                cifs_update_eof(cifsi, offset, bytes_written);
>
>                                if (rc || bytes_written < bytes_to_write) {
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 82d8383..1f09c76 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -800,7 +800,7 @@ set_via_filehandle:
>        if (open_file == NULL)
>                CIFSSMBClose(xid, pTcon, netfid);
>        else
> -               atomic_dec(&open_file->wrtPending);
> +               cifsFileInfo_put(open_file);
>  out:
>        return rc;
>  }
> @@ -1635,7 +1635,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
>                __u32 npid = open_file->pid;
>                rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
>                                        npid, false);
> -               atomic_dec(&open_file->wrtPending);
> +               cifsFileInfo_put(open_file);
>                cFYI(1, ("SetFSize for attrs rc = %d", rc));
>                if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
>                        unsigned int bytes_written;
> @@ -1790,7 +1790,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
>                u16 nfid = open_file->netfid;
>                u32 npid = open_file->pid;
>                rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
> -               atomic_dec(&open_file->wrtPending);
> +               cifsFileInfo_put(open_file);
>        } else {
>                rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
>                                    cifs_sb->local_nls,
>
> --
> David Kleikamp
> IBM Linux Technology Center
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.