Commit 37faf07b authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.6-rc4-ksmbd-server-fixes' of git://git.samba.org/ksmbd

Pull smb server fixes from Steve French:
 "Six SMB3 server fixes for various races found by RO0T Lab of Huawei:

   - Fix oops when racing between oplock break ack and freeing file

   - Simultaneous request fixes for parallel logoffs, and for parallel
     lock requests

   - Fixes for tree disconnect race, session expire race, and close/open
     race"

* tag '6.6-rc4-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix race condition between tree conn lookup and disconnect
  ksmbd: fix race condition from parallel smb2 lock requests
  ksmbd: fix race condition from parallel smb2 logoff requests
  ksmbd: fix uaf in smb20_oplock_break_ack
  ksmbd: fix race condition with fp
  ksmbd: fix race condition between session lookup and expire
parents f707e40d 33b235a6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -84,6 +84,8 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
	spin_lock_init(&conn->llist_lock);
	INIT_LIST_HEAD(&conn->lock_list);

	init_rwsem(&conn->session_lock);

	down_write(&conn_list_lock);
	list_add(&conn->conns_list, &conn_list);
	up_write(&conn_list_lock);
+1 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ struct ksmbd_conn {
	struct nls_table		*local_nls;
	struct unicode_map		*um;
	struct list_head		conns_list;
	struct rw_semaphore		session_lock;
	/* smb session 1 per user */
	struct xarray			sessions;
	unsigned long			last_active;
+39 −3
Original line number Diff line number Diff line
@@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,

	tree_conn->user = sess->user;
	tree_conn->share_conf = sc;
	tree_conn->t_state = TREE_NEW;
	status.tree_conn = tree_conn;
	atomic_set(&tree_conn->refcount, 1);
	init_waitqueue_head(&tree_conn->refcount_q);

	ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
			      GFP_KERNEL));
@@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
	return status;
}

void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
{
	/*
	 * Checking waitqueue to releasing tree connect on
	 * tree disconnect. waitqueue_active is safe because it
	 * uses atomic operation for condition.
	 */
	if (!atomic_dec_return(&tcon->refcount) &&
	    waitqueue_active(&tcon->refcount_q))
		wake_up(&tcon->refcount_q);
}

int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
			       struct ksmbd_tree_connect *tree_conn)
{
	int ret;

	write_lock(&sess->tree_conns_lock);
	xa_erase(&sess->tree_conns, tree_conn->id);
	write_unlock(&sess->tree_conns_lock);

	if (!atomic_dec_and_test(&tree_conn->refcount))
		wait_event(tree_conn->refcount_q,
			   atomic_read(&tree_conn->refcount) == 0);

	ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
	ksmbd_release_tree_conn_id(sess, tree_conn->id);
	xa_erase(&sess->tree_conns, tree_conn->id);
	ksmbd_share_config_put(tree_conn->share_conf);
	kfree(tree_conn);
	return ret;
@@ -111,11 +133,15 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
{
	struct ksmbd_tree_connect *tcon;

	read_lock(&sess->tree_conns_lock);
	tcon = xa_load(&sess->tree_conns, id);
	if (tcon) {
		if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
		if (tcon->t_state != TREE_CONNECTED)
			tcon = NULL;
		else if (!atomic_inc_not_zero(&tcon->refcount))
			tcon = NULL;
	}
	read_unlock(&sess->tree_conns_lock);

	return tcon;
}
@@ -129,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
	if (!sess)
		return -EINVAL;

	xa_for_each(&sess->tree_conns, id, tc)
	xa_for_each(&sess->tree_conns, id, tc) {
		write_lock(&sess->tree_conns_lock);
		if (tc->t_state == TREE_DISCONNECTED) {
			write_unlock(&sess->tree_conns_lock);
			ret = -ENOENT;
			continue;
		}
		tc->t_state = TREE_DISCONNECTED;
		write_unlock(&sess->tree_conns_lock);

		ret |= ksmbd_tree_conn_disconnect(sess, tc);
	}
	xa_destroy(&sess->tree_conns);
	return ret;
}
+9 −2
Original line number Diff line number Diff line
@@ -14,7 +14,11 @@ struct ksmbd_share_config;
struct ksmbd_user;
struct ksmbd_conn;

#define TREE_CONN_EXPIRE		1
enum {
	TREE_NEW = 0,
	TREE_CONNECTED,
	TREE_DISCONNECTED
};

struct ksmbd_tree_connect {
	int				id;
@@ -27,7 +31,9 @@ struct ksmbd_tree_connect {

	int				maximal_access;
	bool				posix_extensions;
	unsigned long			status;
	atomic_t			refcount;
	wait_queue_head_t		refcount_q;
	unsigned int			t_state;
};

struct ksmbd_tree_conn_status {
@@ -46,6 +52,7 @@ struct ksmbd_session;
struct ksmbd_tree_conn_status
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
			const char *share_name);
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);

int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
			       struct ksmbd_tree_connect *tree_conn);
+8 −3
Original line number Diff line number Diff line
@@ -174,7 +174,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
	unsigned long id;
	struct ksmbd_session *sess;

	down_write(&sessions_table_lock);
	down_write(&conn->session_lock);
	xa_for_each(&conn->sessions, id, sess) {
		if (sess->state != SMB2_SESSION_VALID ||
		    time_after(jiffies,
@@ -185,7 +185,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
			continue;
		}
	}
	up_write(&sessions_table_lock);
	up_write(&conn->session_lock);
}

int ksmbd_session_register(struct ksmbd_conn *conn,
@@ -227,7 +227,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
			}
		}
	}
	up_write(&sessions_table_lock);

	down_write(&conn->session_lock);
	xa_for_each(&conn->sessions, id, sess) {
		unsigned long chann_id;
		struct channel *chann;
@@ -244,7 +246,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
			ksmbd_session_destroy(sess);
		}
	}
	up_write(&sessions_table_lock);
	up_write(&conn->session_lock);
}

struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
@@ -252,9 +254,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
{
	struct ksmbd_session *sess;

	down_read(&conn->session_lock);
	sess = xa_load(&conn->sessions, id);
	if (sess)
		sess->last_active = jiffies;
	up_read(&conn->session_lock);
	return sess;
}

@@ -351,6 +355,7 @@ static struct ksmbd_session *__session_create(int protocol)
	xa_init(&sess->ksmbd_chann_list);
	xa_init(&sess->rpc_handle_list);
	sess->sequence_number = 1;
	rwlock_init(&sess->tree_conns_lock);

	ret = __init_smb2_session(sess);
	if (ret)
Loading