Commit cd89d487 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag '6.17-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Two unlink fixes: one for rename and one for deferred close

 - Four smbdirect/RDMA fixes: fix buffer leak in negotiate, two fixes
   for races in smbd_destroy, fix offset and length checks in recv_done

* tag '6.17-rc6-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: fix smbdirect_recv_io leak in smbd_negotiate() error path
  smb: client: fix file open check in __cifs_unlink()
  smb: client: let smbd_destroy() call disable_work_sync(&info->post_send_credits_work)
  smb: client: use disable[_delayed]_work_sync in smbdirect.c
  smb: client: fix filename matching of deferred files
  smb: client: let recv_done verify data_offset, data_length and remaining_data_length
parents 497b9a7b daac51c7
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -312,8 +312,8 @@ extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);

extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);

extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
				const char *path);
void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
					   struct dentry *dentry);

extern void cifs_mark_open_handles_for_deleted_file(struct inode *inode,
				const char *path);
+18 −5
Original line number Diff line number Diff line
@@ -1984,7 +1984,7 @@ static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyren
	}

	netfs_wait_for_outstanding_io(inode);
	cifs_close_deferred_file_under_dentry(tcon, full_path);
	cifs_close_deferred_file_under_dentry(tcon, dentry);
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
	if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
				le64_to_cpu(tcon->fsUnixInfo.Capability))) {
@@ -2003,8 +2003,21 @@ static int __cifs_unlink(struct inode *dir, struct dentry *dentry, bool sillyren
		goto psx_del_no_retry;
	}

	if (sillyrename || (server->vals->protocol_id > SMB10_PROT_ID &&
			    d_is_positive(dentry) && d_count(dentry) > 2))
	/* For SMB2+, if the file is open, we always perform a silly rename.
	 *
	 * We check for d_count() right after calling
	 * cifs_close_deferred_file_under_dentry() to make sure that the
	 * dentry's refcount gets dropped in case the file had any deferred
	 * close.
	 */
	if (!sillyrename && server->vals->protocol_id > SMB10_PROT_ID) {
		spin_lock(&dentry->d_lock);
		if (d_count(dentry) > 1)
			sillyrename = true;
		spin_unlock(&dentry->d_lock);
	}

	if (sillyrename)
		rc = -EBUSY;
	else
		rc = server->ops->unlink(xid, tcon, full_path, cifs_sb, dentry);
@@ -2538,10 +2551,10 @@ cifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
		goto cifs_rename_exit;
	}

	cifs_close_deferred_file_under_dentry(tcon, from_name);
	cifs_close_deferred_file_under_dentry(tcon, source_dentry);
	if (d_inode(target_dentry) != NULL) {
		netfs_wait_for_outstanding_io(d_inode(target_dentry));
		cifs_close_deferred_file_under_dentry(tcon, to_name);
		cifs_close_deferred_file_under_dentry(tcon, target_dentry);
	}

	rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
+16 −22
Original line number Diff line number Diff line
@@ -832,22 +832,19 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon)
		kfree(tmp_list);
	}
}
void
cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)

void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon,
					   struct dentry *dentry)
{
	struct cifsFileInfo *cfile;
	struct file_list *tmp_list, *tmp_next_list;
	void *page;
	const char *full_path;
	struct cifsFileInfo *cfile;
	LIST_HEAD(file_head);

	page = alloc_dentry_path();
	spin_lock(&tcon->open_file_lock);
	list_for_each_entry(cfile, &tcon->openFileList, tlist) {
		full_path = build_path_from_dentry(cfile->dentry, page);
		if (strstr(full_path, path)) {
			if (delayed_work_pending(&cfile->deferred)) {
				if (cancel_delayed_work(&cfile->deferred)) {
		if ((cfile->dentry == dentry) &&
		    delayed_work_pending(&cfile->deferred) &&
		    cancel_delayed_work(&cfile->deferred)) {
			spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
			cifs_del_deferred_close(cfile);
			spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
@@ -859,8 +856,6 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
			list_add_tail(&tmp_list->list, &file_head);
		}
	}
		}
	}
	spin_unlock(&tcon->open_file_lock);

	list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) {
@@ -868,7 +863,6 @@ cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, const char *path)
		list_del(&tmp_list->list);
		kfree(tmp_list);
	}
	free_dentry_path(page);
}

/*
+28 −5
Original line number Diff line number Diff line
@@ -453,9 +453,12 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
	struct smbdirect_recv_io *response =
		container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
	struct smbdirect_socket *sc = response->socket;
	struct smbdirect_socket_parameters *sp = &sc->parameters;
	struct smbd_connection *info =
		container_of(sc, struct smbd_connection, socket);
	int data_length = 0;
	u32 data_offset = 0;
	u32 data_length = 0;
	u32 remaining_data_length = 0;

	log_rdma_recv(INFO, "response=0x%p type=%d wc status=%d wc opcode %d byte_len=%d pkey_index=%u\n",
		      response, sc->recv_io.expected, wc->status, wc->opcode,
@@ -487,7 +490,22 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
	/* SMBD data transfer packet */
	case SMBDIRECT_EXPECT_DATA_TRANSFER:
		data_transfer = smbdirect_recv_io_payload(response);

		if (wc->byte_len <
		    offsetof(struct smbdirect_data_transfer, padding))
			goto error;

		remaining_data_length = le32_to_cpu(data_transfer->remaining_data_length);
		data_offset = le32_to_cpu(data_transfer->data_offset);
		data_length = le32_to_cpu(data_transfer->data_length);
		if (wc->byte_len < data_offset ||
		    (u64)wc->byte_len < (u64)data_offset + data_length)
			goto error;

		if (remaining_data_length > sp->max_fragmented_recv_size ||
		    data_length > sp->max_fragmented_recv_size ||
		    (u64)remaining_data_length + (u64)data_length > (u64)sp->max_fragmented_recv_size)
			goto error;

		if (data_length) {
			if (sc->recv_io.reassembly.full_packet_received)
@@ -1090,8 +1108,10 @@ static int smbd_negotiate(struct smbd_connection *info)
	log_rdma_event(INFO, "smbd_post_recv rc=%d iov.addr=0x%llx iov.length=%u iov.lkey=0x%x\n",
		       rc, response->sge.addr,
		       response->sge.length, response->sge.lkey);
	if (rc)
	if (rc) {
		put_receive_buffer(info, response);
		return rc;
	}

	init_completion(&info->negotiate_completion);
	info->negotiate_done = false;
@@ -1329,13 +1349,16 @@ void smbd_destroy(struct TCP_Server_Info *server)
			sc->status == SMBDIRECT_SOCKET_DISCONNECTED);
	}

	log_rdma_event(INFO, "cancelling post_send_credits_work\n");
	disable_work_sync(&info->post_send_credits_work);

	log_rdma_event(INFO, "destroying qp\n");
	ib_drain_qp(sc->ib.qp);
	rdma_destroy_qp(sc->rdma.cm_id);
	sc->ib.qp = NULL;

	log_rdma_event(INFO, "cancelling idle timer\n");
	cancel_delayed_work_sync(&info->idle_timer_work);
	disable_delayed_work_sync(&info->idle_timer_work);

	/* It's not possible for upper layer to get to reassembly */
	log_rdma_event(INFO, "drain the reassembly queue\n");
@@ -1708,7 +1731,7 @@ static struct smbd_connection *_smbd_get_connection(
	return NULL;

negotiation_failed:
	cancel_delayed_work_sync(&info->idle_timer_work);
	disable_delayed_work_sync(&info->idle_timer_work);
	destroy_caches_and_workqueue(info);
	sc->status = SMBDIRECT_SOCKET_NEGOTIATE_FAILED;
	rdma_disconnect(sc->rdma.cm_id);
@@ -2067,7 +2090,7 @@ static void destroy_mr_list(struct smbd_connection *info)
	struct smbdirect_socket *sc = &info->socket;
	struct smbd_mr *mr, *tmp;

	cancel_work_sync(&info->mr_recovery_work);
	disable_work_sync(&info->mr_recovery_work);
	list_for_each_entry_safe(mr, tmp, &info->mr_list, list) {
		if (mr->state == MR_INVALIDATED)
			ib_dma_unmap_sg(sc->ib.dev, mr->sgt.sgl,