Commit 90153f92 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.1-rc4-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs fixes from Steve French:
 "One symlink handling fix and two fixes foir multichannel issues with
  iterating channels, including for oplock breaks when leases are
  disabled"

* tag '6.1-rc4-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix use-after-free on the link name
  cifs: avoid unnecessary iteration of tcp sessions
  cifs: always iterate smb sessions using primary channel
parents 8391aa4b 542228db
Loading
Loading
Loading
Loading
+25 −1
Original line number Diff line number Diff line
@@ -1143,8 +1143,32 @@ const struct inode_operations cifs_file_inode_ops = {
	.fiemap = cifs_fiemap,
};

const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
			    struct delayed_call *done)
{
	char *target_path;

	target_path = kmalloc(PATH_MAX, GFP_KERNEL);
	if (!target_path)
		return ERR_PTR(-ENOMEM);

	spin_lock(&inode->i_lock);
	if (likely(CIFS_I(inode)->symlink_target)) {
		strscpy(target_path, CIFS_I(inode)->symlink_target, PATH_MAX);
	} else {
		kfree(target_path);
		target_path = ERR_PTR(-EOPNOTSUPP);
	}
	spin_unlock(&inode->i_lock);

	if (!IS_ERR(target_path))
		set_delayed_call(done, kfree_link, target_path);

	return target_path;
}

const struct inode_operations cifs_symlink_inode_ops = {
	.get_link = simple_get_link,
	.get_link = cifs_get_link,
	.permission = cifs_permission,
	.listxattr = cifs_listxattr,
};
+0 −5
Original line number Diff line number Diff line
@@ -215,11 +215,6 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
		kfree(cifs_i->symlink_target);
		cifs_i->symlink_target = fattr->cf_symlink_target;
		fattr->cf_symlink_target = NULL;

		if (unlikely(!cifs_i->symlink_target))
			inode->i_link = ERR_PTR(-EOPNOTSUPP);
		else
			inode->i_link = cifs_i->symlink_target;
	}
	spin_unlock(&inode->i_lock);

+5 −1
Original line number Diff line number Diff line
@@ -400,6 +400,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
{
	struct smb_hdr *buf = (struct smb_hdr *)buffer;
	struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
	struct TCP_Server_Info *pserver;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	struct cifsInodeInfo *pCifsInode;
@@ -464,9 +465,12 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
	if (!(pSMB->LockType & LOCKING_ANDX_OPLOCK_RELEASE))
		return false;

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(srv) ? srv->primary_server : srv;

	/* look up tcon based on tid & uid */
	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(ses, &srv->smb_ses_list, smb_ses_list) {
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
			if (tcon->tid != buf->Tid)
				continue;
+45 −36
Original line number Diff line number Diff line
@@ -135,6 +135,7 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
int
smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
{
	struct TCP_Server_Info *pserver;
	struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
	struct smb2_pdu *pdu = (struct smb2_pdu *)shdr;
	int hdr_size = sizeof(struct smb2_hdr);
@@ -143,6 +144,9 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)
	__u32 calc_len; /* calculated length */
	__u64 mid;

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;

	/*
	 * Add function to do table lookup of StructureSize by command
	 * ie Validate the wct via smb2_struct_sizes table above
@@ -155,7 +159,7 @@ smb2_check_message(char *buf, unsigned int len, struct TCP_Server_Info *server)

		/* decrypt frame now that it is completely read in */
		spin_lock(&cifs_tcp_ses_lock);
		list_for_each_entry(iter, &server->smb_ses_list, smb_ses_list) {
		list_for_each_entry(iter, &pserver->smb_ses_list, smb_ses_list) {
			if (iter->Suid == le64_to_cpu(thdr->SessionId)) {
				ses = iter;
				break;
@@ -608,20 +612,22 @@ smb2_tcon_find_pending_open_lease(struct cifs_tcon *tcon,
}

static bool
smb2_is_valid_lease_break(char *buffer)
smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
{
	struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
	struct TCP_Server_Info *server;
	struct TCP_Server_Info *pserver;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	struct cifs_pending_open *open;

	cifs_dbg(FYI, "Checking for lease break\n");

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;

	/* look up tcon based on tid & uid */
	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
			spin_lock(&tcon->open_file_lock);
			cifs_stats_inc(
@@ -655,7 +661,6 @@ smb2_is_valid_lease_break(char *buffer)
			}
		}
	}
	}
	spin_unlock(&cifs_tcp_ses_lock);
	cifs_dbg(FYI, "Can not process lease break - no lease matched\n");
	trace_smb3_lease_not_found(le32_to_cpu(rsp->CurrentLeaseState),
@@ -671,6 +676,7 @@ bool
smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
{
	struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer;
	struct TCP_Server_Info *pserver;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;
	struct cifsInodeInfo *cinode;
@@ -684,16 +690,19 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
	if (rsp->StructureSize !=
				smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
		if (le16_to_cpu(rsp->StructureSize) == 44)
			return smb2_is_valid_lease_break(buffer);
			return smb2_is_valid_lease_break(buffer, server);
		else
			return false;
	}

	cifs_dbg(FYI, "oplock level 0x%x\n", rsp->OplockLevel);

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;

	/* look up tcon based on tid & uid */
	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {

			spin_lock(&tcon->open_file_lock);
+18 −12
Original line number Diff line number Diff line
@@ -2302,14 +2302,18 @@ static void
smb2_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
{
	struct smb2_hdr *shdr = (struct smb2_hdr *)buf;
	struct TCP_Server_Info *pserver;
	struct cifs_ses *ses;
	struct cifs_tcon *tcon;

	if (shdr->Status != STATUS_NETWORK_NAME_DELETED)
		return;

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;

	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
			if (tcon->tid == le32_to_cpu(shdr->Id.SyncId.TreeId)) {
				spin_lock(&tcon->tc_lock);
@@ -4264,12 +4268,15 @@ init_sg(int num_rqst, struct smb_rqst *rqst, u8 *sign)
static int
smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
{
	struct TCP_Server_Info *pserver;
	struct cifs_ses *ses;
	u8 *ses_enc_key;

	/* If server is a channel, select the primary channel */
	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;

	spin_lock(&cifs_tcp_ses_lock);
	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
		if (ses->Suid == ses_id) {
			spin_lock(&ses->ses_lock);
			ses_enc_key = enc ? ses->smb3encryptionkey :
@@ -4280,7 +4287,6 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
			return 0;
		}
	}
	}
	spin_unlock(&cifs_tcp_ses_lock);

	return -EAGAIN;
Loading