Skip to content

Commit 55ddcff

Browse files
committed
Merge tag '6.17-rc1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull smb client fixes from Steve French: - Fix unlink race and rename races - SMB3.1.1 compression fix - Avoid unneeded strlen calls in cifs_get_spnego_key - Fix slab out of bounds in parse_server_interfaces() - Fix mid leak and server buffer leak - smbdirect send error path fix - update internal version # - Fix unneeded response time update in negotiate protocol * tag '6.17-rc1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: smb: client: remove redundant lstrp update in negotiate protocol cifs: update internal version number smb: client: don't wait for info->send_pending == 0 on error smb: client: fix mid_q_entry memleak leak with per-mid locking smb3: fix for slab out of bounds on mount to ksmbd cifs: avoid extra calls to strlen() in cifs_get_spnego_key() cifs: Fix collect_sample() to handle any iterator type smb: client: fix race with concurrent opens in rename(2) smb: client: fix race with concurrent opens in unlink(2)
2 parents d7ee5bd + e19d8dd commit 55ddcff

File tree

11 files changed

+128
-110
lines changed

11 files changed

+128
-110
lines changed

fs/smb/client/cifs_spnego.c

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -124,55 +124,44 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
124124
dp = description;
125125
/* start with version and hostname portion of UNC string */
126126
spnego_key = ERR_PTR(-EINVAL);
127-
sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
128-
hostname);
129-
dp = description + strlen(description);
127+
dp += sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
128+
hostname);
130129

131130
/* add the server address */
132131
if (server->dstaddr.ss_family == AF_INET)
133-
sprintf(dp, "ip4=%pI4", &sa->sin_addr);
132+
dp += sprintf(dp, "ip4=%pI4", &sa->sin_addr);
134133
else if (server->dstaddr.ss_family == AF_INET6)
135-
sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
134+
dp += sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
136135
else
137136
goto out;
138137

139-
dp = description + strlen(description);
140-
141138
/* for now, only sec=krb5 and sec=mskrb5 and iakerb are valid */
142139
if (server->sec_kerberos)
143-
sprintf(dp, ";sec=krb5");
140+
dp += sprintf(dp, ";sec=krb5");
144141
else if (server->sec_mskerberos)
145-
sprintf(dp, ";sec=mskrb5");
142+
dp += sprintf(dp, ";sec=mskrb5");
146143
else if (server->sec_iakerb)
147-
sprintf(dp, ";sec=iakerb");
144+
dp += sprintf(dp, ";sec=iakerb");
148145
else {
149146
cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
150-
sprintf(dp, ";sec=krb5");
147+
dp += sprintf(dp, ";sec=krb5");
151148
}
152149

153-
dp = description + strlen(description);
154-
sprintf(dp, ";uid=0x%x",
155-
from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
150+
dp += sprintf(dp, ";uid=0x%x",
151+
from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
156152

157-
dp = description + strlen(description);
158-
sprintf(dp, ";creduid=0x%x",
153+
dp += sprintf(dp, ";creduid=0x%x",
159154
from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
160155

161-
if (sesInfo->user_name) {
162-
dp = description + strlen(description);
163-
sprintf(dp, ";user=%s", sesInfo->user_name);
164-
}
156+
if (sesInfo->user_name)
157+
dp += sprintf(dp, ";user=%s", sesInfo->user_name);
165158

166-
dp = description + strlen(description);
167-
sprintf(dp, ";pid=0x%x", current->pid);
159+
dp += sprintf(dp, ";pid=0x%x", current->pid);
168160

169-
if (sesInfo->upcall_target == UPTARGET_MOUNT) {
170-
dp = description + strlen(description);
171-
sprintf(dp, ";upcall_target=mount");
172-
} else {
173-
dp = description + strlen(description);
174-
sprintf(dp, ";upcall_target=app");
175-
}
161+
if (sesInfo->upcall_target == UPTARGET_MOUNT)
162+
dp += sprintf(dp, ";upcall_target=mount");
163+
else
164+
dp += sprintf(dp, ";upcall_target=app");
176165

177166
cifs_dbg(FYI, "key description = %s\n", description);
178167
saved_cred = override_creds(spnego_cred);

fs/smb/client/cifsfs.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,6 @@ extern const struct export_operations cifs_export_ops;
145145
#endif /* CONFIG_CIFS_NFSD_EXPORT */
146146

147147
/* when changing internal version - update following two lines at same time */
148-
#define SMB3_PRODUCT_BUILD 55
149-
#define CIFS_VERSION "2.55"
148+
#define SMB3_PRODUCT_BUILD 56
149+
#define CIFS_VERSION "2.56"
150150
#endif /* _CIFSFS_H */

fs/smb/client/cifsglob.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,7 @@ struct mid_q_entry {
17321732
int mid_rc; /* rc for MID_RC */
17331733
__le16 command; /* smb command code */
17341734
unsigned int optype; /* operation type */
1735+
spinlock_t mid_lock;
17351736
bool wait_cancelled:1; /* Cancelled while waiting for response */
17361737
bool deleted_from_q:1; /* Whether Mid has been dequeued frem pending_mid_q */
17371738
bool large_buf:1; /* if valid response, is pointer to large buf */
@@ -2036,6 +2037,9 @@ require use of the stronger protocol */
20362037
* cifsFileInfo->file_info_lock cifsFileInfo->count cifs_new_fileinfo
20372038
* ->invalidHandle initiate_cifs_search
20382039
* ->oplock_break_cancelled
2040+
* mid_q_entry->mid_lock mid_q_entry->callback alloc_mid
2041+
* smb2_mid_entry_alloc
2042+
* (Any fields of mid_q_entry that will need protection)
20392043
****************************************************************************/
20402044

20412045
#ifdef DECLARE_GLOBALS_HERE
@@ -2375,6 +2379,23 @@ static inline bool cifs_netbios_name(const char *name, size_t namelen)
23752379
return ret;
23762380
}
23772381

2382+
/*
2383+
* Execute mid callback atomically - ensures callback runs exactly once
2384+
* and prevents sleeping in atomic context.
2385+
*/
2386+
static inline void mid_execute_callback(struct mid_q_entry *mid)
2387+
{
2388+
void (*callback)(struct mid_q_entry *mid);
2389+
2390+
spin_lock(&mid->mid_lock);
2391+
callback = mid->callback;
2392+
mid->callback = NULL; /* Mark as executed, */
2393+
spin_unlock(&mid->mid_lock);
2394+
2395+
if (callback)
2396+
callback(mid);
2397+
}
2398+
23782399
#define CIFS_REPARSE_SUPPORT(tcon) \
23792400
((tcon)->posix_extensions || \
23802401
(le32_to_cpu((tcon)->fsAttrInfo.Attributes) & \

fs/smb/client/cifstransport.c

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ alloc_mid(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)
4646
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
4747
memset(temp, 0, sizeof(struct mid_q_entry));
4848
kref_init(&temp->refcount);
49+
spin_lock_init(&temp->mid_lock);
4950
temp->mid = get_mid(smb_buffer);
5051
temp->pid = current->pid;
5152
temp->command = cpu_to_le16(smb_buffer->Command);
@@ -345,16 +346,15 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
345346
rc = wait_for_response(server, midQ);
346347
if (rc != 0) {
347348
send_cancel(server, &rqst, midQ);
348-
spin_lock(&server->mid_queue_lock);
349-
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
350-
midQ->mid_state == MID_RESPONSE_RECEIVED) {
349+
spin_lock(&midQ->mid_lock);
350+
if (midQ->callback) {
351351
/* no longer considered to be "in-flight" */
352352
midQ->callback = release_mid;
353-
spin_unlock(&server->mid_queue_lock);
353+
spin_unlock(&midQ->mid_lock);
354354
add_credits(server, &credits, 0);
355355
return rc;
356356
}
357-
spin_unlock(&server->mid_queue_lock);
357+
spin_unlock(&midQ->mid_lock);
358358
}
359359

360360
rc = cifs_sync_mid_result(midQ, server);
@@ -527,15 +527,14 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
527527
rc = wait_for_response(server, midQ);
528528
if (rc) {
529529
send_cancel(server, &rqst, midQ);
530-
spin_lock(&server->mid_queue_lock);
531-
if (midQ->mid_state == MID_REQUEST_SUBMITTED ||
532-
midQ->mid_state == MID_RESPONSE_RECEIVED) {
530+
spin_lock(&midQ->mid_lock);
531+
if (midQ->callback) {
533532
/* no longer considered to be "in-flight" */
534533
midQ->callback = release_mid;
535-
spin_unlock(&server->mid_queue_lock);
534+
spin_unlock(&midQ->mid_lock);
536535
return rc;
537536
}
538-
spin_unlock(&server->mid_queue_lock);
537+
spin_unlock(&midQ->mid_lock);
539538
}
540539

541540
/* We got the response - restart system call. */

fs/smb/client/compress.c

Lines changed: 21 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -155,58 +155,29 @@ static int cmp_bkt(const void *_a, const void *_b)
155155
}
156156

157157
/*
158-
* TODO:
159-
* Support other iter types, if required.
160-
* Only ITER_XARRAY is supported for now.
158+
* Collect some 2K samples with 2K gaps between.
161159
*/
162-
static int collect_sample(const struct iov_iter *iter, ssize_t max, u8 *sample)
160+
static int collect_sample(const struct iov_iter *source, ssize_t max, u8 *sample)
163161
{
164-
struct folio *folios[16], *folio;
165-
unsigned int nr, i, j, npages;
166-
loff_t start = iter->xarray_start + iter->iov_offset;
167-
pgoff_t last, index = start / PAGE_SIZE;
168-
size_t len, off, foff;
169-
void *p;
170-
int s = 0;
171-
172-
last = (start + max - 1) / PAGE_SIZE;
173-
do {
174-
nr = xa_extract(iter->xarray, (void **)folios, index, last, ARRAY_SIZE(folios),
175-
XA_PRESENT);
176-
if (nr == 0)
177-
return -EIO;
178-
179-
for (i = 0; i < nr; i++) {
180-
folio = folios[i];
181-
npages = folio_nr_pages(folio);
182-
foff = start - folio_pos(folio);
183-
off = foff % PAGE_SIZE;
184-
185-
for (j = foff / PAGE_SIZE; j < npages; j++) {
186-
size_t len2;
187-
188-
len = min_t(size_t, max, PAGE_SIZE - off);
189-
len2 = min_t(size_t, len, SZ_2K);
190-
191-
p = kmap_local_page(folio_page(folio, j));
192-
memcpy(&sample[s], p, len2);
193-
kunmap_local(p);
194-
195-
s += len2;
196-
197-
if (len2 < SZ_2K || s >= max - SZ_2K)
198-
return s;
199-
200-
max -= len;
201-
if (max <= 0)
202-
return s;
203-
204-
start += len;
205-
off = 0;
206-
index++;
207-
}
208-
}
209-
} while (nr == ARRAY_SIZE(folios));
162+
struct iov_iter iter = *source;
163+
size_t s = 0;
164+
165+
while (iov_iter_count(&iter) >= SZ_2K) {
166+
size_t part = umin(umin(iov_iter_count(&iter), SZ_2K), max);
167+
size_t n;
168+
169+
n = copy_from_iter(sample + s, part, &iter);
170+
if (n != part)
171+
return -EFAULT;
172+
173+
s += n;
174+
max -= n;
175+
176+
if (iov_iter_count(&iter) < PAGE_SIZE - SZ_2K)
177+
break;
178+
179+
iov_iter_advance(&iter, SZ_2K);
180+
}
210181

211182
return s;
212183
}

fs/smb/client/connect.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ cifs_abort_connection(struct TCP_Server_Info *server)
335335
cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
336336
list_for_each_entry_safe(mid, nmid, &retry_list, qhead) {
337337
list_del_init(&mid->qhead);
338-
mid->callback(mid);
338+
mid_execute_callback(mid);
339339
release_mid(mid);
340340
}
341341

@@ -919,7 +919,7 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
919919
list_del_init(&mid->qhead);
920920
mid->mid_rc = mid_rc;
921921
mid->mid_state = MID_RC;
922-
mid->callback(mid);
922+
mid_execute_callback(mid);
923923
release_mid(mid);
924924
}
925925

@@ -1117,7 +1117,7 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
11171117
mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
11181118
cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid);
11191119
list_del_init(&mid_entry->qhead);
1120-
mid_entry->callback(mid_entry);
1120+
mid_execute_callback(mid_entry);
11211121
release_mid(mid_entry);
11221122
}
11231123
/* 1/8th of sec is more than enough time for them to exit */
@@ -1394,7 +1394,7 @@ cifs_demultiplex_thread(void *p)
13941394
}
13951395

13961396
if (!mids[i]->multiRsp || mids[i]->multiEnd)
1397-
mids[i]->callback(mids[i]);
1397+
mid_execute_callback(mids[i]);
13981398

13991399
release_mid(mids[i]);
14001400
} else if (server->ops->is_oplock_break &&
@@ -4205,7 +4205,6 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
42054205
return 0;
42064206
}
42074207

4208-
server->lstrp = jiffies;
42094208
server->tcpStatus = CifsInNegotiate;
42104209
server->neg_start = jiffies;
42114210
spin_unlock(&server->srv_lock);

fs/smb/client/inode.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,15 +1943,24 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
19431943
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
19441944
struct tcon_link *tlink;
19451945
struct cifs_tcon *tcon;
1946+
__u32 dosattr = 0, origattr = 0;
19461947
struct TCP_Server_Info *server;
19471948
struct iattr *attrs = NULL;
1948-
__u32 dosattr = 0, origattr = 0;
1949+
bool rehash = false;
19491950

19501951
cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
19511952

19521953
if (unlikely(cifs_forced_shutdown(cifs_sb)))
19531954
return -EIO;
19541955

1956+
/* Unhash dentry in advance to prevent any concurrent opens */
1957+
spin_lock(&dentry->d_lock);
1958+
if (!d_unhashed(dentry)) {
1959+
__d_drop(dentry);
1960+
rehash = true;
1961+
}
1962+
spin_unlock(&dentry->d_lock);
1963+
19551964
tlink = cifs_sb_tlink(cifs_sb);
19561965
if (IS_ERR(tlink))
19571966
return PTR_ERR(tlink);
@@ -2003,7 +2012,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20032012
cifs_drop_nlink(inode);
20042013
}
20052014
} else if (rc == -ENOENT) {
2006-
d_drop(dentry);
2015+
if (simple_positive(dentry))
2016+
d_delete(dentry);
20072017
} else if (rc == -EBUSY) {
20082018
if (server->ops->rename_pending_delete) {
20092019
rc = server->ops->rename_pending_delete(full_path,
@@ -2056,6 +2066,8 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
20562066
kfree(attrs);
20572067
free_xid(xid);
20582068
cifs_put_tlink(tlink);
2069+
if (rehash)
2070+
d_rehash(dentry);
20592071
return rc;
20602072
}
20612073

@@ -2462,6 +2474,7 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24622474
struct cifs_sb_info *cifs_sb;
24632475
struct tcon_link *tlink;
24642476
struct cifs_tcon *tcon;
2477+
bool rehash = false;
24652478
unsigned int xid;
24662479
int rc, tmprc;
24672480
int retry_count = 0;
@@ -2477,6 +2490,17 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
24772490
if (unlikely(cifs_forced_shutdown(cifs_sb)))
24782491
return -EIO;
24792492

2493+
/*
2494+
* Prevent any concurrent opens on the target by unhashing the dentry.
2495+
* VFS already unhashes the target when renaming directories.
2496+
*/
2497+
if (d_is_positive(target_dentry) && !d_is_dir(target_dentry)) {
2498+
if (!d_unhashed(target_dentry)) {
2499+
d_drop(target_dentry);
2500+
rehash = true;
2501+
}
2502+
}
2503+
24802504
tlink = cifs_sb_tlink(cifs_sb);
24812505
if (IS_ERR(tlink))
24822506
return PTR_ERR(tlink);
@@ -2518,6 +2542,8 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25182542
}
25192543
}
25202544

2545+
if (!rc)
2546+
rehash = false;
25212547
/*
25222548
* No-replace is the natural behavior for CIFS, so skip unlink hacks.
25232549
*/
@@ -2576,12 +2602,16 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
25762602
goto cifs_rename_exit;
25772603
rc = cifs_do_rename(xid, source_dentry, from_name,
25782604
target_dentry, to_name);
2605+
if (!rc)
2606+
rehash = false;
25792607
}
25802608

25812609
/* force revalidate to go get info when needed */
25822610
CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
25832611

25842612
cifs_rename_exit:
2613+
if (rehash)
2614+
d_rehash(target_dentry);
25852615
kfree(info_buf_source);
25862616
free_dentry_path(page2);
25872617
free_dentry_path(page1);

0 commit comments

Comments
 (0)