Commit 53ff5cf8 authored by Namjae Jeon's avatar Namjae Jeon Committed by Steve French
Browse files

ksmbd: fix race condition between session lookup and expire



 Thread A                        +  Thread B
 ksmbd_session_lookup            |  smb2_sess_setup
   sess = xa_load                |
                                 |
                                 |    xa_erase(&conn->sessions, sess->id);
                                 |
                                 |    ksmbd_session_destroy(sess) --> kfree(sess)
                                 |
   // UAF!                       |
   sess->last_active = jiffies   |
                                 +

This patch add rwsem to fix race condition between ksmbd_session_lookup
and ksmbd_expire_session.

Reported-by: default avatarluosili <rootlab@huawei.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 8a749fd1
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;
+7 −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;
}