Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8fd99d4 authored by Sachin Prabhu's avatar Sachin Prabhu Committed by Greg Kroah-Hartman
Browse files

Handle mismatched open calls



commit 38bd49064a1ecb67baad33598e3d824448ab11ec upstream.

A signal can interrupt a SendReceive call which result in incoming
responses to the call being ignored. This is a problem for calls such as
open which results in the successful response being ignored. This
results in an open file resource on the server.

The patch looks into responses which were cancelled after being sent and
in case of successful open closes the open fids.

For this patch, the check is only done in SendReceive2()

RH-bz: 1403319

Signed-off-by: default avatarSachin Prabhu <sprabhu@redhat.com>
Reviewed-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Acked-by: default avatarSachin Prabhu <sprabhu@redhat.com>
Signed-off-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 00cca976
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -241,6 +241,7 @@ struct smb_version_operations {
	/* verify the message */
	int (*check_message)(char *, unsigned int, struct TCP_Server_Info *);
	bool (*is_oplock_break)(char *, struct TCP_Server_Info *);
	int (*handle_cancelled_mid)(char *, struct TCP_Server_Info *);
	void (*downgrade_oplock)(struct TCP_Server_Info *,
					struct cifsInodeInfo *, bool);
	/* process transaction2 response */
@@ -1314,12 +1315,19 @@ struct mid_q_entry {
	void *callback_data;	  /* general purpose pointer for callback */
	void *resp_buf;		/* pointer to received SMB header */
	int mid_state;	/* wish this were enum but can not pass to wait_event */
	unsigned int mid_flags;
	__le16 command;		/* smb command code */
	bool large_buf:1;	/* if valid response, is pointer to large buf */
	bool multiRsp:1;	/* multiple trans2 responses for one request  */
	bool multiEnd:1;	/* both received */
};

struct close_cancelled_open {
	struct cifs_fid         fid;
	struct cifs_tcon        *tcon;
	struct work_struct      work;
};

/*	Make code in transport.c a little cleaner by moving
	update of optional stats into function below */
#ifdef CONFIG_CIFS_STATS2
@@ -1451,6 +1459,9 @@ static inline void free_dfs_info_array(struct dfs_info3_param *param,
#define   MID_RESPONSE_MALFORMED 0x10
#define   MID_SHUTDOWN		 0x20

/* Flags */
#define   MID_WAIT_CANCELLED	 1 /* Cancelled while waiting for response */

/* Types of response buffer returned from SendReceive2 */
#define   CIFS_NO_BUFFER        0    /* Response buffer not returned */
#define   CIFS_SMALL_BUFFER     1
+4 −0
Original line number Diff line number Diff line
@@ -1423,6 +1423,8 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)

	length = discard_remaining_data(server);
	dequeue_mid(mid, rdata->result);
	mid->resp_buf = server->smallbuf;
	server->smallbuf = NULL;
	return length;
}

@@ -1534,6 +1536,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
		return cifs_readv_discard(server, mid);

	dequeue_mid(mid, false);
	mid->resp_buf = server->smallbuf;
	server->smallbuf = NULL;
	return length;
}

+11 −2
Original line number Diff line number Diff line
@@ -882,10 +882,19 @@ cifs_demultiplex_thread(void *p)

		server->lstrp = jiffies;
		if (mid_entry != NULL) {
			if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
			     mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
					server->ops->handle_cancelled_mid)
				server->ops->handle_cancelled_mid(
							mid_entry->resp_buf,
							server);

			if (!mid_entry->multiRsp || mid_entry->multiEnd)
				mid_entry->callback(mid_entry);
		} else if (!server->ops->is_oplock_break ||
			   !server->ops->is_oplock_break(buf, server)) {
		} else if (server->ops->is_oplock_break &&
			   server->ops->is_oplock_break(buf, server)) {
			cifs_dbg(FYI, "Received oplock break\n");
		} else {
			cifs_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
				 atomic_read(&midCount));
			cifs_dump_mem("Received Data is: ", buf,
+44 −0
Original line number Diff line number Diff line
@@ -654,3 +654,47 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
	cifs_dbg(FYI, "Can not process oplock break for non-existent connection\n");
	return false;
}

void
smb2_cancelled_close_fid(struct work_struct *work)
{
	struct close_cancelled_open *cancelled = container_of(work,
					struct close_cancelled_open, work);

	cifs_dbg(VFS, "Close unmatched open\n");

	SMB2_close(0, cancelled->tcon, cancelled->fid.persistent_fid,
		   cancelled->fid.volatile_fid);
	cifs_put_tcon(cancelled->tcon);
	kfree(cancelled);
}

int
smb2_handle_cancelled_mid(char *buffer, struct TCP_Server_Info *server)
{
	struct smb2_hdr *hdr = (struct smb2_hdr *)buffer;
	struct smb2_create_rsp *rsp = (struct smb2_create_rsp *)buffer;
	struct cifs_tcon *tcon;
	struct close_cancelled_open *cancelled;

	if (hdr->Command != SMB2_CREATE || hdr->Status != STATUS_SUCCESS)
		return 0;

	cancelled = kzalloc(sizeof(*cancelled), GFP_KERNEL);
	if (!cancelled)
		return -ENOMEM;

	tcon = smb2_find_smb_tcon(server, hdr->SessionId, hdr->TreeId);
	if (!tcon) {
		kfree(cancelled);
		return -ENOENT;
	}

	cancelled->fid.persistent_fid = rsp->PersistentFileId;
	cancelled->fid.volatile_fid = rsp->VolatileFileId;
	cancelled->tcon = tcon;
	INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
	queue_work(cifsiod_wq, &cancelled->work);

	return 0;
}
+4 −0
Original line number Diff line number Diff line
@@ -1565,6 +1565,7 @@ struct smb_version_operations smb20_operations = {
	.clear_stats = smb2_clear_stats,
	.print_stats = smb2_print_stats,
	.is_oplock_break = smb2_is_valid_oplock_break,
	.handle_cancelled_mid = smb2_handle_cancelled_mid,
	.downgrade_oplock = smb2_downgrade_oplock,
	.need_neg = smb2_need_neg,
	.negotiate = smb2_negotiate,
@@ -1645,6 +1646,7 @@ struct smb_version_operations smb21_operations = {
	.clear_stats = smb2_clear_stats,
	.print_stats = smb2_print_stats,
	.is_oplock_break = smb2_is_valid_oplock_break,
	.handle_cancelled_mid = smb2_handle_cancelled_mid,
	.downgrade_oplock = smb2_downgrade_oplock,
	.need_neg = smb2_need_neg,
	.negotiate = smb2_negotiate,
@@ -1727,6 +1729,7 @@ struct smb_version_operations smb30_operations = {
	.print_stats = smb2_print_stats,
	.dump_share_caps = smb2_dump_share_caps,
	.is_oplock_break = smb2_is_valid_oplock_break,
	.handle_cancelled_mid = smb2_handle_cancelled_mid,
	.downgrade_oplock = smb2_downgrade_oplock,
	.need_neg = smb2_need_neg,
	.negotiate = smb2_negotiate,
@@ -1815,6 +1818,7 @@ struct smb_version_operations smb311_operations = {
	.print_stats = smb2_print_stats,
	.dump_share_caps = smb2_dump_share_caps,
	.is_oplock_break = smb2_is_valid_oplock_break,
	.handle_cancelled_mid = smb2_handle_cancelled_mid,
	.downgrade_oplock = smb2_downgrade_oplock,
	.need_neg = smb2_need_neg,
	.negotiate = smb2_negotiate,
Loading