Message ID | 20191128001839.5926-1-pshilov@microsoft.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | CIFS: Fix NULL-pointer dereference in smb2_push_mandatory_locks | expand |
Merged into cifs-2.6.git for-next On Wed, Nov 27, 2019 at 6:18 PM Pavel Shilovsky <piastryyy@gmail.com> wrote: > > Currently when the client creates a cifsFileInfo structure for > a newly opened file, it allocates a list of byte-range locks > with a pointer to the new cfile and attaches this list to the > inode's lock list. The latter happens before initializing all > other fields, e.g. cfile->tlink. Thus a partially initialized > cifsFileInfo structure becomes available to other threads that > walk through the inode's lock list. One example of such a thread > may be an oplock break worker thread that tries to push all > cached byte-range locks. This causes NULL-pointer dereference > in smb2_push_mandatory_locks() when accessing cfile->tlink: > > [598428.945633] BUG: kernel NULL pointer dereference, address: 0000000000000038 > ... > [598428.945749] Workqueue: cifsoplockd cifs_oplock_break [cifs] > [598428.945793] RIP: 0010:smb2_push_mandatory_locks+0xd6/0x5a0 [cifs] > ... > [598428.945834] Call Trace: > [598428.945870] ? cifs_revalidate_mapping+0x45/0x90 [cifs] > [598428.945901] cifs_oplock_break+0x13d/0x450 [cifs] > [598428.945909] process_one_work+0x1db/0x380 > [598428.945914] worker_thread+0x4d/0x400 > [598428.945921] kthread+0x104/0x140 > [598428.945925] ? process_one_work+0x380/0x380 > [598428.945931] ? kthread_park+0x80/0x80 > [598428.945937] ret_from_fork+0x35/0x40 > > Fix this by reordering initialization steps of the cifsFileInfo > structure: initialize all the fields first and then add the new > byte-range lock list to the inode's lock list. > > Cc: Stable <stable@vger.kernel.org> > Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com> > --- > fs/cifs/file.c | 7 ++++--- > 1 file changed, 4 insertions(+), 3 deletions(-) > > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index 520fbe4d42b9..069635ec9d94 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -313,9 +313,6 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, > INIT_LIST_HEAD(&fdlocks->locks); > fdlocks->cfile = cfile; > cfile->llist = fdlocks; > - cifs_down_write(&cinode->lock_sem); > - list_add(&fdlocks->llist, &cinode->llist); > - up_write(&cinode->lock_sem); > > cfile->count = 1; > cfile->pid = current->tgid; > @@ -339,6 +336,10 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, > oplock = 0; > } > > + cifs_down_write(&cinode->lock_sem); > + list_add(&fdlocks->llist, &cinode->llist); > + up_write(&cinode->lock_sem); > + > spin_lock(&tcon->open_file_lock); > if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) > oplock = fid->pending_open->oplock; > -- > 2.17.1 >
Pavel Shilovsky <piastryyy@gmail.com> writes: > Currently when the client creates a cifsFileInfo structure for > a newly opened file, it allocates a list of byte-range locks > with a pointer to the new cfile and attaches this list to the > inode's lock list. The latter happens before initializing all > other fields, e.g. cfile->tlink. Thus a partially initialized > cifsFileInfo structure becomes available to other threads that > walk through the inode's lock list. One example of such a thread > may be an oplock break worker thread that tries to push all > cached byte-range locks. This causes NULL-pointer dereference > in smb2_push_mandatory_locks() when accessing cfile->tlink: reviewing late but this makes sense. Reviewed-by: Aurelien Aptel <aaptel@suse.com>
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 520fbe4d42b9..069635ec9d94 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -313,9 +313,6 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, INIT_LIST_HEAD(&fdlocks->locks); fdlocks->cfile = cfile; cfile->llist = fdlocks; - cifs_down_write(&cinode->lock_sem); - list_add(&fdlocks->llist, &cinode->llist); - up_write(&cinode->lock_sem); cfile->count = 1; cfile->pid = current->tgid; @@ -339,6 +336,10 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, oplock = 0; } + cifs_down_write(&cinode->lock_sem); + list_add(&fdlocks->llist, &cinode->llist); + up_write(&cinode->lock_sem); + spin_lock(&tcon->open_file_lock); if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock) oplock = fid->pending_open->oplock;
Currently when the client creates a cifsFileInfo structure for a newly opened file, it allocates a list of byte-range locks with a pointer to the new cfile and attaches this list to the inode's lock list. The latter happens before initializing all other fields, e.g. cfile->tlink. Thus a partially initialized cifsFileInfo structure becomes available to other threads that walk through the inode's lock list. One example of such a thread may be an oplock break worker thread that tries to push all cached byte-range locks. This causes NULL-pointer dereference in smb2_push_mandatory_locks() when accessing cfile->tlink: [598428.945633] BUG: kernel NULL pointer dereference, address: 0000000000000038 ... [598428.945749] Workqueue: cifsoplockd cifs_oplock_break [cifs] [598428.945793] RIP: 0010:smb2_push_mandatory_locks+0xd6/0x5a0 [cifs] ... [598428.945834] Call Trace: [598428.945870] ? cifs_revalidate_mapping+0x45/0x90 [cifs] [598428.945901] cifs_oplock_break+0x13d/0x450 [cifs] [598428.945909] process_one_work+0x1db/0x380 [598428.945914] worker_thread+0x4d/0x400 [598428.945921] kthread+0x104/0x140 [598428.945925] ? process_one_work+0x380/0x380 [598428.945931] ? kthread_park+0x80/0x80 [598428.945937] ret_from_fork+0x35/0x40 Fix this by reordering initialization steps of the cifsFileInfo structure: initialize all the fields first and then add the new byte-range lock list to the inode's lock list. Cc: Stable <stable@vger.kernel.org> Signed-off-by: Pavel Shilovsky <pshilov@microsoft.com> --- fs/cifs/file.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)