Skip to content

Commit c9a2e4a

Browse files
committed
Merge tag '5.4-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "Seven cifs/smb3 fixes, including three for stable" * tag '5.4-rc5-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: Fix cifsInodeInfo lock_sem deadlock when reconnect occurs CIFS: Fix use after free of file info structures CIFS: Fix retry mid list corruption on reconnects cifs: Fix missed free operations CIFS: avoid using MID 0xFFFF cifs: clarify comment about timestamp granularity for old servers cifs: Handle -EINPROGRESS only when noblockcnt is set
2 parents 6995a6a + d46b0da commit c9a2e4a

File tree

9 files changed

+75
-37
lines changed

9 files changed

+75
-37
lines changed

fs/cifs/cifsfs.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,13 @@ cifs_read_super(struct super_block *sb)
169169
else
170170
sb->s_maxbytes = MAX_NON_LFS;
171171

172-
/* Some very old servers like DOS and OS/2 used 2 second granularity */
172+
/*
173+
* Some very old servers like DOS and OS/2 used 2 second granularity
174+
* (while all current servers use 100ns granularity - see MS-DTYP)
175+
* but 1 second is the maximum allowed granularity for the VFS
176+
* so for old servers set time granularity to 1 second while for
177+
* everything else (current servers) set it to 100ns.
178+
*/
173179
if ((tcon->ses->server->vals->protocol_id == SMB10_PROT_ID) &&
174180
((tcon->ses->capabilities &
175181
tcon->ses->server->vals->cap_nt_find) == 0) &&

fs/cifs/cifsglob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,11 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
13911391
struct cifsInodeInfo {
13921392
bool can_cache_brlcks;
13931393
struct list_head llist; /* locks helb by this inode */
1394+
/*
1395+
* NOTE: Some code paths call down_read(lock_sem) twice, so
1396+
* we must always use use cifs_down_write() instead of down_write()
1397+
* for this semaphore to avoid deadlocks.
1398+
*/
13941399
struct rw_semaphore lock_sem; /* protect the fields above */
13951400
/* BB add in lists for dirty pages i.e. write caching info for oplock */
13961401
struct list_head openFileList;

fs/cifs/cifsproto.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ extern int cifs_unlock_range(struct cifsFileInfo *cfile,
170170
struct file_lock *flock, const unsigned int xid);
171171
extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
172172

173+
extern void cifs_down_write(struct rw_semaphore *sem);
173174
extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
174175
struct file *file,
175176
struct tcon_link *tlink,

fs/cifs/connect.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,9 +564,11 @@ cifs_reconnect(struct TCP_Server_Info *server)
564564
spin_lock(&GlobalMid_Lock);
565565
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
566566
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
567+
kref_get(&mid_entry->refcount);
567568
if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
568569
mid_entry->mid_state = MID_RETRY_NEEDED;
569570
list_move(&mid_entry->qhead, &retry_list);
571+
mid_entry->mid_flags |= MID_DELETED;
570572
}
571573
spin_unlock(&GlobalMid_Lock);
572574
mutex_unlock(&server->srv_mutex);
@@ -576,6 +578,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
576578
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
577579
list_del_init(&mid_entry->qhead);
578580
mid_entry->callback(mid_entry);
581+
cifs_mid_q_entry_release(mid_entry);
579582
}
580583

581584
if (cifs_rdma_enabled(server)) {
@@ -895,8 +898,10 @@ dequeue_mid(struct mid_q_entry *mid, bool malformed)
895898
if (mid->mid_flags & MID_DELETED)
896899
printk_once(KERN_WARNING
897900
"trying to dequeue a deleted mid\n");
898-
else
901+
else {
899902
list_del_init(&mid->qhead);
903+
mid->mid_flags |= MID_DELETED;
904+
}
900905
spin_unlock(&GlobalMid_Lock);
901906
}
902907

@@ -966,8 +971,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
966971
list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
967972
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
968973
cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid);
974+
kref_get(&mid_entry->refcount);
969975
mid_entry->mid_state = MID_SHUTDOWN;
970976
list_move(&mid_entry->qhead, &dispose_list);
977+
mid_entry->mid_flags |= MID_DELETED;
971978
}
972979
spin_unlock(&GlobalMid_Lock);
973980

@@ -977,6 +984,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
977984
cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid);
978985
list_del_init(&mid_entry->qhead);
979986
mid_entry->callback(mid_entry);
987+
cifs_mid_q_entry_release(mid_entry);
980988
}
981989
/* 1/8th of sec is more than enough time for them to exit */
982990
msleep(125);
@@ -3882,8 +3890,12 @@ generic_ip_connect(struct TCP_Server_Info *server)
38823890

38833891
rc = socket->ops->connect(socket, saddr, slen,
38843892
server->noblockcnt ? O_NONBLOCK : 0);
3885-
3886-
if (rc == -EINPROGRESS)
3893+
/*
3894+
* When mounting SMB root file systems, we do not want to block in
3895+
* connect. Otherwise bail out and then let cifs_reconnect() perform
3896+
* reconnect failover - if possible.
3897+
*/
3898+
if (server->noblockcnt && rc == -EINPROGRESS)
38873899
rc = 0;
38883900
if (rc < 0) {
38893901
cifs_dbg(FYI, "Error %d connecting to server\n", rc);

fs/cifs/file.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@ cifs_has_mand_locks(struct cifsInodeInfo *cinode)
281281
return has_locks;
282282
}
283283

284+
void
285+
cifs_down_write(struct rw_semaphore *sem)
286+
{
287+
while (!down_write_trylock(sem))
288+
msleep(10);
289+
}
290+
284291
struct cifsFileInfo *
285292
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
286293
struct tcon_link *tlink, __u32 oplock)
@@ -306,7 +313,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
306313
INIT_LIST_HEAD(&fdlocks->locks);
307314
fdlocks->cfile = cfile;
308315
cfile->llist = fdlocks;
309-
down_write(&cinode->lock_sem);
316+
cifs_down_write(&cinode->lock_sem);
310317
list_add(&fdlocks->llist, &cinode->llist);
311318
up_write(&cinode->lock_sem);
312319

@@ -405,10 +412,11 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
405412
bool oplock_break_cancelled;
406413

407414
spin_lock(&tcon->open_file_lock);
408-
415+
spin_lock(&cifsi->open_file_lock);
409416
spin_lock(&cifs_file->file_info_lock);
410417
if (--cifs_file->count > 0) {
411418
spin_unlock(&cifs_file->file_info_lock);
419+
spin_unlock(&cifsi->open_file_lock);
412420
spin_unlock(&tcon->open_file_lock);
413421
return;
414422
}
@@ -421,9 +429,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
421429
cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
422430

423431
/* remove it from the lists */
424-
spin_lock(&cifsi->open_file_lock);
425432
list_del(&cifs_file->flist);
426-
spin_unlock(&cifsi->open_file_lock);
427433
list_del(&cifs_file->tlist);
428434
atomic_dec(&tcon->num_local_opens);
429435

@@ -440,6 +446,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
440446
cifs_set_oplock_level(cifsi, 0);
441447
}
442448

449+
spin_unlock(&cifsi->open_file_lock);
443450
spin_unlock(&tcon->open_file_lock);
444451

445452
oplock_break_cancelled = wait_oplock_handler ?
@@ -464,7 +471,7 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, bool wait_oplock_handler)
464471
* Delete any outstanding lock records. We'll lose them when the file
465472
* is closed anyway.
466473
*/
467-
down_write(&cifsi->lock_sem);
474+
cifs_down_write(&cifsi->lock_sem);
468475
list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
469476
list_del(&li->llist);
470477
cifs_del_lock_waiters(li);
@@ -1027,7 +1034,7 @@ static void
10271034
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
10281035
{
10291036
struct cifsInodeInfo *cinode = CIFS_I(d_inode(cfile->dentry));
1030-
down_write(&cinode->lock_sem);
1037+
cifs_down_write(&cinode->lock_sem);
10311038
list_add_tail(&lock->llist, &cfile->llist->locks);
10321039
up_write(&cinode->lock_sem);
10331040
}
@@ -1049,7 +1056,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
10491056

10501057
try_again:
10511058
exist = false;
1052-
down_write(&cinode->lock_sem);
1059+
cifs_down_write(&cinode->lock_sem);
10531060

10541061
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
10551062
lock->type, lock->flags, &conf_lock,
@@ -1072,7 +1079,7 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
10721079
(lock->blist.next == &lock->blist));
10731080
if (!rc)
10741081
goto try_again;
1075-
down_write(&cinode->lock_sem);
1082+
cifs_down_write(&cinode->lock_sem);
10761083
list_del_init(&lock->blist);
10771084
}
10781085

@@ -1125,7 +1132,7 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
11251132
return rc;
11261133

11271134
try_again:
1128-
down_write(&cinode->lock_sem);
1135+
cifs_down_write(&cinode->lock_sem);
11291136
if (!cinode->can_cache_brlcks) {
11301137
up_write(&cinode->lock_sem);
11311138
return rc;
@@ -1331,7 +1338,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
13311338
int rc = 0;
13321339

13331340
/* we are going to update can_cache_brlcks here - need a write access */
1334-
down_write(&cinode->lock_sem);
1341+
cifs_down_write(&cinode->lock_sem);
13351342
if (!cinode->can_cache_brlcks) {
13361343
up_write(&cinode->lock_sem);
13371344
return rc;
@@ -1522,7 +1529,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
15221529
if (!buf)
15231530
return -ENOMEM;
15241531

1525-
down_write(&cinode->lock_sem);
1532+
cifs_down_write(&cinode->lock_sem);
15261533
for (i = 0; i < 2; i++) {
15271534
cur = buf;
15281535
num = 0;

fs/cifs/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2475,9 +2475,9 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
24752475
rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);
24762476
cifsFileInfo_put(wfile);
24772477
if (rc)
2478-
return rc;
2478+
goto cifs_setattr_exit;
24792479
} else if (rc != -EBADF)
2480-
return rc;
2480+
goto cifs_setattr_exit;
24812481
else
24822482
rc = 0;
24832483
}

fs/cifs/smb1ops.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
171171
/* we do not want to loop forever */
172172
last_mid = cur_mid;
173173
cur_mid++;
174+
/* avoid 0xFFFF MID */
175+
if (cur_mid == 0xffff)
176+
cur_mid++;
174177

175178
/*
176179
* This nested loop looks more expensive than it is.

fs/cifs/smb2file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
145145

146146
cur = buf;
147147

148-
down_write(&cinode->lock_sem);
148+
cifs_down_write(&cinode->lock_sem);
149149
list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
150150
if (flock->fl_start > li->offset ||
151151
(flock->fl_start + length) <

fs/cifs/transport.c

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,8 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
8686

8787
static void _cifs_mid_q_entry_release(struct kref *refcount)
8888
{
89-
struct mid_q_entry *mid = container_of(refcount, struct mid_q_entry,
90-
refcount);
91-
92-
mempool_free(mid, cifs_mid_poolp);
93-
}
94-
95-
void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
96-
{
97-
spin_lock(&GlobalMid_Lock);
98-
kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
99-
spin_unlock(&GlobalMid_Lock);
100-
}
101-
102-
void
103-
DeleteMidQEntry(struct mid_q_entry *midEntry)
104-
{
89+
struct mid_q_entry *midEntry =
90+
container_of(refcount, struct mid_q_entry, refcount);
10591
#ifdef CONFIG_CIFS_STATS2
10692
__le16 command = midEntry->server->vals->lock_cmd;
10793
__u16 smb_cmd = le16_to_cpu(midEntry->command);
@@ -166,15 +152,30 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
166152
}
167153
}
168154
#endif
155+
156+
mempool_free(midEntry, cifs_mid_poolp);
157+
}
158+
159+
void cifs_mid_q_entry_release(struct mid_q_entry *midEntry)
160+
{
161+
spin_lock(&GlobalMid_Lock);
162+
kref_put(&midEntry->refcount, _cifs_mid_q_entry_release);
163+
spin_unlock(&GlobalMid_Lock);
164+
}
165+
166+
void DeleteMidQEntry(struct mid_q_entry *midEntry)
167+
{
169168
cifs_mid_q_entry_release(midEntry);
170169
}
171170

172171
void
173172
cifs_delete_mid(struct mid_q_entry *mid)
174173
{
175174
spin_lock(&GlobalMid_Lock);
176-
list_del_init(&mid->qhead);
177-
mid->mid_flags |= MID_DELETED;
175+
if (!(mid->mid_flags & MID_DELETED)) {
176+
list_del_init(&mid->qhead);
177+
mid->mid_flags |= MID_DELETED;
178+
}
178179
spin_unlock(&GlobalMid_Lock);
179180

180181
DeleteMidQEntry(mid);
@@ -872,7 +873,10 @@ cifs_sync_mid_result(struct mid_q_entry *mid, struct TCP_Server_Info *server)
872873
rc = -EHOSTDOWN;
873874
break;
874875
default:
875-
list_del_init(&mid->qhead);
876+
if (!(mid->mid_flags & MID_DELETED)) {
877+
list_del_init(&mid->qhead);
878+
mid->mid_flags |= MID_DELETED;
879+
}
876880
cifs_server_dbg(VFS, "%s: invalid mid state mid=%llu state=%d\n",
877881
__func__, mid->mid, mid->mid_state);
878882
rc = -EIO;

0 commit comments

Comments
 (0)