Skip to content

Commit

Permalink
ksmbd: fix use-after-free in ksmbd_free_work_struct
Browse files Browse the repository at this point in the history
->interim_entry of ksmbd_work could be deleted after oplock is freed.
We don't need to manage it with linked list. The interim request could be
immediately sent whenever a oplock break wait is needed.

Cc: stable@vger.kernel.org
Reported-by: Norbert Szetei <norbert@doyensec.com>
Tested-by: Norbert Szetei <norbert@doyensec.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Namjae Jeon authored and Steve French committed Mar 10, 2025
1 parent 80e54e8 commit bb39ed4
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 27 deletions.
3 changes: 0 additions & 3 deletions fs/smb/server/ksmbd_work.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
INIT_LIST_HEAD(&work->request_entry);
INIT_LIST_HEAD(&work->async_request_entry);
INIT_LIST_HEAD(&work->fp_entry);
INIT_LIST_HEAD(&work->interim_entry);
INIT_LIST_HEAD(&work->aux_read_list);
work->iov_alloc_cnt = 4;
work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
Expand Down Expand Up @@ -56,8 +55,6 @@ void ksmbd_free_work_struct(struct ksmbd_work *work)
kfree(work->tr_buf);
kvfree(work->request_buf);
kfree(work->iov);
if (!list_empty(&work->interim_entry))
list_del(&work->interim_entry);

if (work->async_id)
ksmbd_release_id(&work->conn->async_ida, work->async_id);
Expand Down
1 change: 0 additions & 1 deletion fs/smb/server/ksmbd_work.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ struct ksmbd_work {
/* List head at conn->async_requests */
struct list_head async_request_entry;
struct list_head fp_entry;
struct list_head interim_entry;
};

/**
Expand Down
37 changes: 15 additions & 22 deletions fs/smb/server/oplock.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work,
opinfo->fid = id;
opinfo->Tid = Tid;
INIT_LIST_HEAD(&opinfo->op_entry);
INIT_LIST_HEAD(&opinfo->interim_list);
init_waitqueue_head(&opinfo->oplock_q);
init_waitqueue_head(&opinfo->oplock_brk);
atomic_set(&opinfo->refcount, 1);
Expand Down Expand Up @@ -803,7 +802,6 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
static int smb2_lease_break_noti(struct oplock_info *opinfo)
{
struct ksmbd_conn *conn = opinfo->conn;
struct list_head *tmp, *t;
struct ksmbd_work *work;
struct lease_break_info *br_info;
struct lease *lease = opinfo->o_lease;
Expand Down Expand Up @@ -831,16 +829,6 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
work->sess = opinfo->sess;

if (opinfo->op_state == OPLOCK_ACK_WAIT) {
list_for_each_safe(tmp, t, &opinfo->interim_list) {
struct ksmbd_work *in_work;

in_work = list_entry(tmp, struct ksmbd_work,
interim_entry);
setup_async_work(in_work, NULL, NULL);
smb2_send_interim_resp(in_work, STATUS_PENDING);
list_del_init(&in_work->interim_entry);
release_async_work(in_work);
}
INIT_WORK(&work->work, __smb2_lease_break_noti);
ksmbd_queue_work(work);
wait_for_break_ack(opinfo);
Expand Down Expand Up @@ -871,7 +859,8 @@ static void wait_lease_breaking(struct oplock_info *opinfo)
}
}

static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level,
struct ksmbd_work *in_work)
{
int err = 0;

Expand Down Expand Up @@ -914,9 +903,15 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
}

if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE |
SMB2_LEASE_HANDLE_CACHING_LE))
SMB2_LEASE_HANDLE_CACHING_LE)) {
if (in_work) {
setup_async_work(in_work, NULL, NULL);
smb2_send_interim_resp(in_work, STATUS_PENDING);
release_async_work(in_work);
}

brk_opinfo->op_state = OPLOCK_ACK_WAIT;
else
} else
atomic_dec(&brk_opinfo->breaking_cnt);
} else {
err = oplock_break_pending(brk_opinfo, req_op_level);
Expand Down Expand Up @@ -1116,7 +1111,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
if (ksmbd_conn_releasing(opinfo->conn))
continue;

oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
opinfo_put(opinfo);
}
}
Expand Down Expand Up @@ -1152,7 +1147,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)

if (ksmbd_conn_releasing(opinfo->conn))
continue;
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL);
opinfo_put(opinfo);
}
}
Expand Down Expand Up @@ -1252,8 +1247,7 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
goto op_break_not_needed;
}

list_add(&work->interim_entry, &prev_opinfo->interim_list);
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II);
err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work);
opinfo_put(prev_opinfo);
if (err == -ENOENT)
goto set_lev;
Expand Down Expand Up @@ -1322,8 +1316,7 @@ static void smb_break_all_write_oplock(struct ksmbd_work *work,
}

brk_opinfo->open_trunc = is_trunc;
list_add(&work->interim_entry, &brk_opinfo->interim_list);
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II);
oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work);
opinfo_put(brk_opinfo);
}

Expand Down Expand Up @@ -1386,7 +1379,7 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
SMB2_LEASE_KEY_SIZE))
goto next;
brk_op->open_trunc = is_trunc;
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE);
oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL);
next:
opinfo_put(brk_op);
rcu_read_lock();
Expand Down
1 change: 0 additions & 1 deletion fs/smb/server/oplock.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ struct oplock_info {
bool is_lease;
bool open_trunc; /* truncate on open */
struct lease *o_lease;
struct list_head interim_list;
struct list_head op_entry;
struct list_head lease_entry;
wait_queue_head_t oplock_q; /* Other server threads */
Expand Down

0 comments on commit bb39ed4

Please sign in to comment.