Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

smb3: missing inode locks in zero range

smb3 fallocate zero range was not grabbing the inode or filemap_invalidate
locks so could have race with pagemap reinstantiating the page.

Cc: stable@vger.kernel.org
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

David Howells and committed by
Steve French
c919c164 1c23f9e6

+30 -25
+30 -25
fs/cifs/smb2ops.c
··· 3307 3307 return pntsd; 3308 3308 } 3309 3309 3310 + static long smb3_zero_data(struct file *file, struct cifs_tcon *tcon, 3311 + loff_t offset, loff_t len, unsigned int xid) 3312 + { 3313 + struct cifsFileInfo *cfile = file->private_data; 3314 + struct file_zero_data_information fsctl_buf; 3315 + 3316 + cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 3317 + 3318 + fsctl_buf.FileOffset = cpu_to_le64(offset); 3319 + fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 3320 + 3321 + return SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 3322 + cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 3323 + (char *)&fsctl_buf, 3324 + sizeof(struct file_zero_data_information), 3325 + 0, NULL, NULL); 3326 + } 3327 + 3310 3328 static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, 3311 3329 loff_t offset, loff_t len, bool keep_size) 3312 3330 { 3313 3331 struct cifs_ses *ses = tcon->ses; 3314 - struct inode *inode; 3315 - struct cifsInodeInfo *cifsi; 3332 + struct inode *inode = file_inode(file); 3333 + struct cifsInodeInfo *cifsi = CIFS_I(inode); 3316 3334 struct cifsFileInfo *cfile = file->private_data; 3317 - struct file_zero_data_information fsctl_buf; 3318 3335 long rc; 3319 3336 unsigned int xid; 3320 3337 __le64 eof; 3321 3338 3322 3339 xid = get_xid(); 3323 3340 3324 - inode = d_inode(cfile->dentry); 3325 - cifsi = CIFS_I(inode); 3326 - 3327 3341 trace_smb3_zero_enter(xid, cfile->fid.persistent_fid, tcon->tid, 3328 3342 ses->Suid, offset, len); 3343 + 3344 + inode_lock(inode); 3345 + filemap_invalidate_lock(inode->i_mapping); 3329 3346 3330 3347 /* 3331 3348 * We zero the range through ioctl, so we need remove the page caches ··· 3351 3334 truncate_pagecache_range(inode, offset, offset + len - 1); 3352 3335 3353 3336 /* if file not oplocked can't be sure whether asking to extend size */ 3354 - if (!CIFS_CACHE_READ(cifsi)) 3355 - if (keep_size == false) { 3356 - rc = -EOPNOTSUPP; 3357 - trace_smb3_zero_err(xid, cfile->fid.persistent_fid, 3358 - tcon->tid, ses->Suid, offset, len, rc); 3359 - free_xid(xid); 3360 - return rc; 3361 - } 3337 + rc = -EOPNOTSUPP; 3338 + if (keep_size == false && !CIFS_CACHE_READ(cifsi)) 3339 + goto zero_range_exit; 3362 3340 3363 - cifs_dbg(FYI, "Offset %lld len %lld\n", offset, len); 3364 - 3365 - fsctl_buf.FileOffset = cpu_to_le64(offset); 3366 - fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); 3367 - 3368 - rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, 3369 - cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, 3370 - (char *)&fsctl_buf, 3371 - sizeof(struct file_zero_data_information), 3372 - 0, NULL, NULL); 3373 - if (rc) 3341 + rc = smb3_zero_data(file, tcon, offset, len, xid); 3342 + if (rc < 0) 3374 3343 goto zero_range_exit; 3375 3344 3376 3345 /* ··· 3369 3366 } 3370 3367 3371 3368 zero_range_exit: 3369 + filemap_invalidate_unlock(inode->i_mapping); 3370 + inode_unlock(inode); 3372 3371 free_xid(xid); 3373 3372 if (rc) 3374 3373 trace_smb3_zero_err(xid, cfile->fid.persistent_fid, tcon->tid,