Skip to content

Commit 39898f0

Browse files
committed
Merge tag '6.12-rc-ksmbd-server-fixes-part1' of git://git.samba.org/ksmbd
Pull smb server updates from Steve French: "Four ksmbd server fixes, three for stable: - Fix an issue where the directory can't be deleted if the share is on a file system that does not provide dot and dotdot entries - Fix file creation failure if the parent name of pathname is case sensitive - Fix write failure with FILE_APPEND_DATA flags - Add reference count to connection struct to protect UAF of oplocks on multichannel" * tag '6.12-rc-ksmbd-server-fixes-part1' of git://git.samba.org/ksmbd: ksmbd: handle caseless file creation ksmbd: make __dir_empty() compatible with POSIX ksmbd: add refcnt to ksmbd_conn struct ksmbd: allow write with FILE_APPEND_DATA
2 parents cc38044 + c5a709f commit 39898f0

File tree

5 files changed

+31
-51
lines changed

5 files changed

+31
-51
lines changed

fs/smb/server/connection.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
3939
xa_destroy(&conn->sessions);
4040
kvfree(conn->request_buf);
4141
kfree(conn->preauth_info);
42-
kfree(conn);
42+
if (atomic_dec_and_test(&conn->refcnt))
43+
kfree(conn);
4344
}
4445

4546
/**
@@ -68,6 +69,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
6869
conn->um = NULL;
6970
atomic_set(&conn->req_running, 0);
7071
atomic_set(&conn->r_count, 0);
72+
atomic_set(&conn->refcnt, 1);
7173
conn->total_credits = 1;
7274
conn->outstanding_credits = 0;
7375

fs/smb/server/connection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct ksmbd_conn {
106106
bool signing_negotiated;
107107
__le16 signing_algorithm;
108108
bool binding;
109+
atomic_t refcnt;
109110
};
110111

111112
struct ksmbd_conn_ops {

fs/smb/server/oplock.c

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
5151
init_waitqueue_head(&opinfo->oplock_brk);
5252
atomic_set(&opinfo->refcount, 1);
5353
atomic_set(&opinfo->breaking_cnt, 0);
54+
atomic_inc(&opinfo->conn->refcnt);
5455

5556
return opinfo;
5657
}
@@ -124,6 +125,8 @@ static void free_opinfo(struct oplock_info *opinfo)
124125
{
125126
if (opinfo->is_lease)
126127
free_lease(opinfo);
128+
if (opinfo->conn && atomic_dec_and_test(&opinfo->conn->refcnt))
129+
kfree(opinfo->conn);
127130
kfree(opinfo);
128131
}
129132

@@ -163,9 +166,7 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
163166
!atomic_inc_not_zero(&opinfo->refcount))
164167
opinfo = NULL;
165168
else {
166-
atomic_inc(&opinfo->conn->r_count);
167169
if (ksmbd_conn_releasing(opinfo->conn)) {
168-
atomic_dec(&opinfo->conn->r_count);
169170
atomic_dec(&opinfo->refcount);
170171
opinfo = NULL;
171172
}
@@ -177,26 +178,11 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
177178
return opinfo;
178179
}
179180

180-
static void opinfo_conn_put(struct oplock_info *opinfo)
181+
void opinfo_put(struct oplock_info *opinfo)
181182
{
182-
struct ksmbd_conn *conn;
183-
184183
if (!opinfo)
185184
return;
186185

187-
conn = opinfo->conn;
188-
/*
189-
* Checking waitqueue to dropping pending requests on
190-
* disconnection. waitqueue_active is safe because it
191-
* uses atomic operation for condition.
192-
*/
193-
if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))
194-
wake_up(&conn->r_count_q);
195-
opinfo_put(opinfo);
196-
}
197-
198-
void opinfo_put(struct oplock_info *opinfo)
199-
{
200186
if (!atomic_dec_and_test(&opinfo->refcount))
201187
return;
202188

@@ -1127,14 +1113,11 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
11271113
if (!atomic_inc_not_zero(&opinfo->refcount))
11281114
continue;
11291115

1130-
atomic_inc(&opinfo->conn->r_count);
1131-
if (ksmbd_conn_releasing(opinfo->conn)) {
1132-
atomic_dec(&opinfo->conn->r_count);
1116+
if (ksmbd_conn_releasing(opinfo->conn))
11331117
continue;
1134-
}
11351118

11361119
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1137-
opinfo_conn_put(opinfo);
1120+
opinfo_put(opinfo);
11381121
}
11391122
}
11401123
up_read(&p_ci->m_lock);
@@ -1167,13 +1150,10 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
11671150
if (!atomic_inc_not_zero(&opinfo->refcount))
11681151
continue;
11691152

1170-
atomic_inc(&opinfo->conn->r_count);
1171-
if (ksmbd_conn_releasing(opinfo->conn)) {
1172-
atomic_dec(&opinfo->conn->r_count);
1153+
if (ksmbd_conn_releasing(opinfo->conn))
11731154
continue;
1174-
}
11751155
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1176-
opinfo_conn_put(opinfo);
1156+
opinfo_put(opinfo);
11771157
}
11781158
}
11791159
up_read(&p_ci->m_lock);
@@ -1252,7 +1232,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
12521232
prev_opinfo = opinfo_get_list(ci);
12531233
if (!prev_opinfo ||
12541234
(prev_opinfo->level == SMB2_OPLOCK_LEVEL_NONE && lctx)) {
1255-
opinfo_conn_put(prev_opinfo);
1235+
opinfo_put(prev_opinfo);
12561236
goto set_lev;
12571237
}
12581238
prev_op_has_lease = prev_opinfo->is_lease;
@@ -1262,19 +1242,19 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
12621242
if (share_ret < 0 &&
12631243
prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
12641244
err = share_ret;
1265-
opinfo_conn_put(prev_opinfo);
1245+
opinfo_put(prev_opinfo);
12661246
goto err_out;
12671247
}
12681248

12691249
if (prev_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
12701250
prev_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1271-
opinfo_conn_put(prev_opinfo);
1251+
opinfo_put(prev_opinfo);
12721252
goto op_break_not_needed;
12731253
}
12741254

12751255
list_add(&work->interim_entry, &prev_opinfo->interim_list);
12761256
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
1277-
opinfo_conn_put(prev_opinfo);
1257+
opinfo_put(prev_opinfo);
12781258
if (err == -ENOENT)
12791259
goto set_lev;
12801260
/* Check all oplock was freed by close */
@@ -1337,14 +1317,14 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
13371317
return;
13381318
if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH &&
13391319
brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
1340-
opinfo_conn_put(brk_opinfo);
1320+
opinfo_put(brk_opinfo);
13411321
return;
13421322
}
13431323

13441324
brk_opinfo->open_trunc = is_trunc;
13451325
list_add(&work->interim_entry, &brk_opinfo->interim_list);
13461326
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
1347-
opinfo_conn_put(brk_opinfo);
1327+
opinfo_put(brk_opinfo);
13481328
}
13491329

13501330
/**
@@ -1376,11 +1356,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13761356
if (!atomic_inc_not_zero(&brk_op->refcount))
13771357
continue;
13781358

1379-
atomic_inc(&brk_op->conn->r_count);
1380-
if (ksmbd_conn_releasing(brk_op->conn)) {
1381-
atomic_dec(&brk_op->conn->r_count);
1359+
if (ksmbd_conn_releasing(brk_op->conn))
13821360
continue;
1383-
}
13841361

13851362
rcu_read_unlock();
13861363
if (brk_op->is_lease && (brk_op->o_lease->state &
@@ -1411,7 +1388,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
14111388
brk_op->open_trunc = is_trunc;
14121389
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
14131390
next:
1414-
opinfo_conn_put(brk_op);
1391+
opinfo_put(brk_op);
14151392
rcu_read_lock();
14161393
}
14171394
rcu_read_unlock();

fs/smb/server/vfs.c

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
496496
int err = 0;
497497

498498
if (work->conn->connection_type) {
499-
if (!(fp->daccess & FILE_WRITE_DATA_LE)) {
499+
if (!(fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE))) {
500500
pr_err("no right to write(%pD)\n", fp->filp);
501501
err = -EACCES;
502502
goto out;
@@ -1115,9 +1115,10 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen,
11151115
struct ksmbd_readdir_data *buf;
11161116

11171117
buf = container_of(ctx, struct ksmbd_readdir_data, ctx);
1118-
buf->dirent_count++;
1118+
if (!is_dot_dotdot(name, namlen))
1119+
buf->dirent_count++;
11191120

1120-
return buf->dirent_count <= 2;
1121+
return !buf->dirent_count;
11211122
}
11221123

11231124
/**
@@ -1137,7 +1138,7 @@ int ksmbd_vfs_empty_dir(struct ksmbd_file *fp)
11371138
readdir_data.dirent_count = 0;
11381139

11391140
err = iterate_dir(fp->filp, &readdir_data.ctx);
1140-
if (readdir_data.dirent_count > 2)
1141+
if (readdir_data.dirent_count)
11411142
err = -ENOTEMPTY;
11421143
else
11431144
err = 0;
@@ -1166,7 +1167,7 @@ static bool __caseless_lookup(struct dir_context *ctx, const char *name,
11661167
if (cmp < 0)
11671168
cmp = strncasecmp((char *)buf->private, name, namlen);
11681169
if (!cmp) {
1169-
memcpy((char *)buf->private, name, namlen);
1170+
memcpy((char *)buf->private, name, buf->used);
11701171
buf->dirent_count = 1;
11711172
return false;
11721173
}
@@ -1234,10 +1235,7 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12341235
char *filepath;
12351236
size_t path_len, remain_len;
12361237

1237-
filepath = kstrdup(name, GFP_KERNEL);
1238-
if (!filepath)
1239-
return -ENOMEM;
1240-
1238+
filepath = name;
12411239
path_len = strlen(filepath);
12421240
remain_len = path_len;
12431241

@@ -1280,10 +1278,9 @@ int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
12801278
err = -EINVAL;
12811279
out2:
12821280
path_put(parent_path);
1283-
out1:
1284-
kfree(filepath);
12851281
}
12861282

1283+
out1:
12871284
if (!err) {
12881285
err = mnt_want_write(parent_path->mnt);
12891286
if (err) {

fs/smb/server/vfs_cache.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,8 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon,
863863
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry) {
864864
if (op->conn != conn)
865865
continue;
866+
if (op->conn && atomic_dec_and_test(&op->conn->refcnt))
867+
kfree(op->conn);
866868
op->conn = NULL;
867869
}
868870
up_write(&ci->m_lock);
@@ -965,6 +967,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp)
965967
if (op->conn)
966968
continue;
967969
op->conn = fp->conn;
970+
atomic_inc(&op->conn->refcnt);
968971
}
969972
up_write(&ci->m_lock);
970973

0 commit comments

Comments
 (0)