Skip to content

Commit 8b8cd4b

Browse files
committed
Merge tag '6.7-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd
Pull smb server fixes from Steve French: - Memory leak fix (in lock error path) - Two fixes for create with allocation size - FIx for potential UAF in lease break error path - Five directory lease (caching) fixes found during additional recent testing * tag '6.7-rc5-ksmbd-server-fixes' of git://git.samba.org/ksmbd: ksmbd: fix wrong name of SMB2_CREATE_ALLOCATION_SIZE ksmbd: fix wrong allocation size update in smb2_open() ksmbd: avoid duplicate opinfo_put() call on error of smb21_lease_break_ack() ksmbd: lazy v2 lease break on smb2_write() ksmbd: send v2 lease break notification for directory ksmbd: downgrade RWH lease caching state to RH for directory ksmbd: set v2 lease capability ksmbd: set epoch in create context v2 lease ksmbd: fix memory leak in smb2_lock()
2 parents 26aff84 + 1373665 commit 8b8cd4b

8 files changed

Lines changed: 171 additions & 45 deletions

File tree

fs/smb/common/smb2pdu.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1145,7 +1145,7 @@ struct smb2_server_client_notification {
11451145
#define SMB2_CREATE_SD_BUFFER "SecD" /* security descriptor */
11461146
#define SMB2_CREATE_DURABLE_HANDLE_REQUEST "DHnQ"
11471147
#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT "DHnC"
1148-
#define SMB2_CREATE_ALLOCATION_SIZE "AISi"
1148+
#define SMB2_CREATE_ALLOCATION_SIZE "AlSi"
11491149
#define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"
11501150
#define SMB2_CREATE_TIMEWARP_REQUEST "TWrp"
11511151
#define SMB2_CREATE_QUERY_ON_DISK_ID "QFid"
@@ -1253,6 +1253,7 @@ struct create_mxac_rsp {
12531253
#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04)
12541254

12551255
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02)
1256+
#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04)
12561257

12571258
#define SMB2_LEASE_KEY_SIZE 16
12581259

fs/smb/server/oplock.c

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
102102
lease->new_state = 0;
103103
lease->flags = lctx->flags;
104104
lease->duration = lctx->duration;
105+
lease->is_dir = lctx->is_dir;
105106
memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
106107
lease->version = lctx->version;
107-
lease->epoch = 0;
108+
lease->epoch = le16_to_cpu(lctx->epoch);
108109
INIT_LIST_HEAD(&opinfo->lease_entry);
109110
opinfo->o_lease = lease;
110111

@@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
395396
{
396397
struct oplock_info *opinfo;
397398

398-
if (S_ISDIR(file_inode(fp->filp)->i_mode))
399-
return;
399+
if (fp->reserve_lease_break)
400+
smb_lazy_parent_lease_break_close(fp);
400401

401402
opinfo = opinfo_get(fp);
402403
if (!opinfo)
@@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
543544
/* upgrading lease */
544545
if ((atomic_read(&ci->op_count) +
545546
atomic_read(&ci->sop_count)) == 1) {
546-
if (lease->state ==
547-
(lctx->req_state & lease->state)) {
547+
if (lease->state != SMB2_LEASE_NONE_LE &&
548+
lease->state == (lctx->req_state & lease->state)) {
548549
lease->state |= lctx->req_state;
549550
if (lctx->req_state &
550551
SMB2_LEASE_WRITE_CACHING_LE)
551552
lease_read_to_write(opinfo);
553+
552554
}
553555
} else if ((atomic_read(&ci->op_count) +
554556
atomic_read(&ci->sop_count)) > 1) {
@@ -900,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
900902
lease->new_state =
901903
SMB2_LEASE_READ_CACHING_LE;
902904
} else {
903-
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
905+
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
906+
!lease->is_dir)
904907
lease->new_state =
905908
SMB2_LEASE_READ_CACHING_LE;
906909
else
@@ -1032,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
10321035
SMB2_LEASE_KEY_SIZE);
10331036
lease2->duration = lease1->duration;
10341037
lease2->flags = lease1->flags;
1038+
lease2->epoch = lease1->epoch++;
10351039
}
10361040

10371041
static int add_lease_global_list(struct oplock_info *opinfo)
@@ -1081,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
10811085
}
10821086
}
10831087

1088+
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
1089+
struct lease_ctx_info *lctx)
1090+
{
1091+
struct oplock_info *opinfo;
1092+
struct ksmbd_inode *p_ci = NULL;
1093+
1094+
if (lctx->version != 2)
1095+
return;
1096+
1097+
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
1098+
if (!p_ci)
1099+
return;
1100+
1101+
read_lock(&p_ci->m_lock);
1102+
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1103+
if (!opinfo->is_lease)
1104+
continue;
1105+
1106+
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
1107+
(!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
1108+
!compare_guid_key(opinfo, fp->conn->ClientGUID,
1109+
lctx->parent_lease_key))) {
1110+
if (!atomic_inc_not_zero(&opinfo->refcount))
1111+
continue;
1112+
1113+
atomic_inc(&opinfo->conn->r_count);
1114+
if (ksmbd_conn_releasing(opinfo->conn)) {
1115+
atomic_dec(&opinfo->conn->r_count);
1116+
continue;
1117+
}
1118+
1119+
read_unlock(&p_ci->m_lock);
1120+
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1121+
opinfo_conn_put(opinfo);
1122+
read_lock(&p_ci->m_lock);
1123+
}
1124+
}
1125+
read_unlock(&p_ci->m_lock);
1126+
1127+
ksmbd_inode_put(p_ci);
1128+
}
1129+
1130+
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
1131+
{
1132+
struct oplock_info *opinfo;
1133+
struct ksmbd_inode *p_ci = NULL;
1134+
1135+
rcu_read_lock();
1136+
opinfo = rcu_dereference(fp->f_opinfo);
1137+
rcu_read_unlock();
1138+
1139+
if (!opinfo->is_lease || opinfo->o_lease->version != 2)
1140+
return;
1141+
1142+
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
1143+
if (!p_ci)
1144+
return;
1145+
1146+
read_lock(&p_ci->m_lock);
1147+
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1148+
if (!opinfo->is_lease)
1149+
continue;
1150+
1151+
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
1152+
if (!atomic_inc_not_zero(&opinfo->refcount))
1153+
continue;
1154+
1155+
atomic_inc(&opinfo->conn->r_count);
1156+
if (ksmbd_conn_releasing(opinfo->conn)) {
1157+
atomic_dec(&opinfo->conn->r_count);
1158+
continue;
1159+
}
1160+
read_unlock(&p_ci->m_lock);
1161+
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
1162+
opinfo_conn_put(opinfo);
1163+
read_lock(&p_ci->m_lock);
1164+
}
1165+
}
1166+
read_unlock(&p_ci->m_lock);
1167+
1168+
ksmbd_inode_put(p_ci);
1169+
}
1170+
10841171
/**
10851172
* smb_grant_oplock() - handle oplock/lease request on file open
10861173
* @work: smb work
@@ -1104,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
11041191
bool prev_op_has_lease;
11051192
__le32 prev_op_state = 0;
11061193

1107-
/* not support directory lease */
1108-
if (S_ISDIR(file_inode(fp->filp)->i_mode))
1109-
return 0;
1110-
11111194
opinfo = alloc_opinfo(work, pid, tid);
11121195
if (!opinfo)
11131196
return -ENOMEM;
@@ -1364,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
13641447
memcpy(buf->lcontext.LeaseKey, lease->lease_key,
13651448
SMB2_LEASE_KEY_SIZE);
13661449
buf->lcontext.LeaseFlags = lease->flags;
1450+
buf->lcontext.Epoch = cpu_to_le16(++lease->epoch);
13671451
buf->lcontext.LeaseState = lease->state;
13681452
memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key,
13691453
SMB2_LEASE_KEY_SIZE);
@@ -1400,10 +1484,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
14001484
/**
14011485
* parse_lease_state() - parse lease context containted in file open request
14021486
* @open_req: buffer containing smb2 file open(create) request
1487+
* @is_dir: whether leasing file is directory
14031488
*
14041489
* Return: oplock state, -ENOENT if create lease context not found
14051490
*/
1406-
struct lease_ctx_info *parse_lease_state(void *open_req)
1491+
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
14071492
{
14081493
struct create_context *cc;
14091494
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
@@ -1421,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
14211506
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
14221507

14231508
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
1424-
lreq->req_state = lc->lcontext.LeaseState;
1509+
if (is_dir) {
1510+
lreq->req_state = lc->lcontext.LeaseState &
1511+
~SMB2_LEASE_WRITE_CACHING_LE;
1512+
lreq->is_dir = true;
1513+
} else
1514+
lreq->req_state = lc->lcontext.LeaseState;
14251515
lreq->flags = lc->lcontext.LeaseFlags;
1516+
lreq->epoch = lc->lcontext.Epoch;
14261517
lreq->duration = lc->lcontext.LeaseDuration;
14271518
memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey,
14281519
SMB2_LEASE_KEY_SIZE);

fs/smb/server/oplock.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ struct lease_ctx_info {
3434
__le32 flags;
3535
__le64 duration;
3636
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
37+
__le16 epoch;
3738
int version;
39+
bool is_dir;
3840
};
3941

4042
struct lease_table {
@@ -53,6 +55,7 @@ struct lease {
5355
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
5456
int version;
5557
unsigned short epoch;
58+
bool is_dir;
5659
struct lease_table *l_lb;
5760
};
5861

@@ -108,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
108111

109112
/* Lease related functions */
110113
void create_lease_buf(u8 *rbuf, struct lease *lease);
111-
struct lease_ctx_info *parse_lease_state(void *open_req);
114+
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
112115
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
113116
int lease_read_to_write(struct oplock_info *opinfo);
114117

@@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
124127
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
125128
struct lease_ctx_info *lctx);
126129
void destroy_lease_table(struct ksmbd_conn *conn);
130+
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
131+
struct lease_ctx_info *lctx);
132+
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
127133
#endif /* __KSMBD_OPLOCK_H */

fs/smb/server/smb2ops.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
221221
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
222222

223223
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
224-
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
224+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
225+
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
225226

226227
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
227228
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
@@ -245,7 +246,8 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
245246
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
246247

247248
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
248-
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
249+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
250+
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
249251

250252
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
251253
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
@@ -270,7 +272,8 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
270272
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
271273

272274
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
273-
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
275+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
276+
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
274277

275278
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
276279
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&

0 commit comments

Comments
 (0)