Re: [PATCH 4/5] cifs: update receive_encrypted_standard to handle compounded responses

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



2018-08-06 20:56 GMT-07:00 Ronnie Sahlberg <lsahlber@xxxxxxxxxx>:
> Signed-off-by: Ronnie Sahlberg <lsahlber@xxxxxxxxxx>
> ---
>  fs/cifs/cifsglob.h  |  5 +++-
>  fs/cifs/connect.c   | 77 +++++++++++++++++++++++++++++++----------------------
>  fs/cifs/smb2ops.c   | 58 +++++++++++++++++++++++++++++++++-------
>  fs/cifs/transport.c |  2 --
>  4 files changed, 98 insertions(+), 44 deletions(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 0553929e8339..2b89cf693243 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -76,6 +76,9 @@
>  #define SMB_ECHO_INTERVAL_MAX 600
>  #define SMB_ECHO_INTERVAL_DEFAULT 60
>
> +/* maximum number of PDUs in one compound */
> +#define MAX_COMPOUND 5
> +
>  /*
>   * Default number of credits to keep available for SMB3.
>   * This value is chosen somewhat arbitrarily. The Windows client
> @@ -458,7 +461,7 @@ struct smb_version_operations {
>                                  struct smb_rqst *, struct smb_rqst *);
>         int (*is_transform_hdr)(void *buf);
>         int (*receive_transform)(struct TCP_Server_Info *,
> -                                struct mid_q_entry **);
> +                                struct mid_q_entry **, int *);
>         enum securityEnum (*select_sectype)(struct TCP_Server_Info *,
>                             enum securityEnum);
>         int (*next_header)(char *);
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index d9bd10d295a9..1d983add05a7 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -850,13 +850,13 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
>  static int
>  cifs_demultiplex_thread(void *p)
>  {
> -       int length;
> +       int i, num_mids, length;
>         struct TCP_Server_Info *server = p;
>         unsigned int pdu_length;
>         unsigned int next_offset;
>         char *buf = NULL;
>         struct task_struct *task_to_wake = NULL;
> -       struct mid_q_entry *mid_entry;
> +       struct mid_q_entry *mids[MAX_COMPOUND];
>
>         current->flags |= PF_MEMALLOC;
>         cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
> @@ -923,24 +923,28 @@ cifs_demultiplex_thread(void *p)
>                                 server->pdu_size = next_offset;
>                 }
>
> -               mid_entry = NULL;
> +               memset(mids, 0, sizeof(mids));
> +               num_mids = 1;

Isn't it cleaner to assume 0 mids here?

> +
>                 if (server->ops->is_transform_hdr &&
>                     server->ops->receive_transform &&
>                     server->ops->is_transform_hdr(buf)) {
>                         length = server->ops->receive_transform(server,
> -                                                               &mid_entry);
> +                                                               mids,
> +                                                               &num_mids);
>                 } else {
> -                       mid_entry = server->ops->find_mid(server, buf);
> +                       mids[0] = server->ops->find_mid(server, buf);
>
> -                       if (!mid_entry || !mid_entry->receive)
> -                               length = standard_receive3(server, mid_entry);
> +                       if (!mids[0] || !mids[0]->receive)
> +                               length = standard_receive3(server, mids[0]);
>                         else
> -                               length = mid_entry->receive(server, mid_entry);
> +                               length = mids[0]->receive(server, mids[0]);

and we can set num_mid to 1 here if mids[0] != NULL.

>                 }
>
>                 if (length < 0) {
> -                       if (mid_entry)
> -                               cifs_mid_q_entry_release(mid_entry);
> +                       for (i = 0; i < num_mids; i++)
> +                               if (mids[i])
> +                                       cifs_mid_q_entry_release(mids[i]);
>                         continue;
>                 }
>
> @@ -948,33 +952,42 @@ cifs_demultiplex_thread(void *p)
>                         buf = server->bigbuf;
>
>                 server->lstrp = jiffies;
> -               if (mid_entry != NULL) {
> -                       mid_entry->resp_buf_size = server->pdu_size;
> -                       if ((mid_entry->mid_flags & MID_WAIT_CANCELLED) &&
> -                            mid_entry->mid_state == MID_RESPONSE_RECEIVED &&
> +               if (buf && server->ops->is_oplock_break &&
> +                   server->ops->is_oplock_break(buf, server)) {

We should execute this only if no mid was found for a particular
response. Also we need a valid buf pointer here pointing to a
decrypted response which is not the case for compounding chain.

> +                       cifs_dbg(FYI, "Received oplock break\n");
> +                       goto finished_mids;
> +               }
> +
> +               for (i = 0; i < num_mids; i++) {
> +                       if (mids[i] == NULL) {
> +                               cifs_dbg(VFS, "No task to wake, unknown frame "
> +                                        "received! NumMids %d\n",
> +                                        atomic_read(&midCount));
> +                               cifs_dump_mem("Received Data is: ", buf,

Here is the same situation as for oplock breaks - a valid buf pointer is needed.


> +                                             HEADER_SIZE(server));
> +#ifdef CONFIG_CIFS_DEBUG2
> +                               if (server->ops->dump_detail)
> +                                       server->ops->dump_detail(buf, server);
> +                               cifs_dump_mids(server);
> +#endif /* CIFS_DEBUG2 */
> +                               break;
> +                       }
> +                       mids[i]->resp_buf_size = server->pdu_size;
> +                       if ((mids[i]->mid_flags & MID_WAIT_CANCELLED) &&
> +                            mids[i]->mid_state == MID_RESPONSE_RECEIVED &&
>                                         server->ops->handle_cancelled_mid)
>                                 server->ops->handle_cancelled_mid(
> -                                                       mid_entry->resp_buf,
> +                                                       mids[i]->resp_buf,
>                                                         server);
>
> -                       if (!mid_entry->multiRsp || mid_entry->multiEnd)
> -                               mid_entry->callback(mid_entry);
> -
> -                       cifs_mid_q_entry_release(mid_entry);
> -               } 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,
> -                                     HEADER_SIZE(server));
> -#ifdef CONFIG_CIFS_DEBUG2
> -                       if (server->ops->dump_detail)
> -                               server->ops->dump_detail(buf, server);
> -                       cifs_dump_mids(server);
> -#endif /* CIFS_DEBUG2 */
> +                       if (!mids[i]->multiRsp || mids[i]->multiEnd)
> +                               mids[i]->callback(mids[i]);
>                 }
> +
> +       finished_mids:
> +               for (i = 0; i < num_mids; i++)
> +                       if (mids[i])
> +                               cifs_mid_q_entry_release(mids[i]);
>                 if (pdu_length > server->pdu_size) {
>                         if (!allocate_buffers(server))
>                                 continue;
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index ebc13ebebddf..0d797eb7d055 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -2942,13 +2942,18 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct mid_q_entry **mid)
>
>  static int
>  receive_encrypted_standard(struct TCP_Server_Info *server,
> -                          struct mid_q_entry **mid)
> +                          struct mid_q_entry **mid, int *num_mids)
>  {
> -       int length;
> +       int ret, length;
>         char *buf = server->smallbuf;
> +       struct smb2_sync_hdr *shdr;
>         unsigned int pdu_length = server->pdu_size;
>         unsigned int buf_size;
>         struct mid_q_entry *mid_entry;
> +       int next_is_large;
> +       char *next_buffer = NULL;
> +
> +       *num_mids = 0;
>
>         /* switch to large buffer if too big for a small one */
>         if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE) {
> @@ -2969,6 +2974,21 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
>         if (length)
>                 return length;
>
> +       next_is_large = server->large_buf;
> + one_more:
> +       shdr = (struct smb2_sync_hdr *)buf;
> +       if (shdr->NextCommand) {
> +               if (next_is_large) {
> +                       next_buffer = (char *)cifs_buf_get();
> +                       memcpy(next_buffer, server->bigbuf + shdr->NextCommand,
> +                              pdu_length - shdr->NextCommand);
> +               } else {
> +                       next_buffer = (char *)cifs_small_buf_get();
> +                       memcpy(next_buffer, server->smallbuf + shdr->NextCommand,
> +                              pdu_length - shdr->NextCommand);
> +               }
> +       }
> +
>         mid_entry = smb2_find_mid(server, buf);
>         if (mid_entry == NULL)
>                 cifs_dbg(FYI, "mid not found\n");
> @@ -2976,17 +2996,36 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
>                 cifs_dbg(FYI, "mid found\n");
>                 mid_entry->decrypted = true;
>         }
> +       mid_entry->resp_buf_size = server->pdu_size;
>
> -       *mid = mid_entry;
> -
> +       mid[(*num_mids)++] = mid_entry;
> +       if (*num_mids >= MAX_COMPOUND) {
> +               cifs_dbg(VFS, "too many PDUs in compound\n");
> +               return -1;
> +       }
>         if (mid_entry && mid_entry->handle)
> -               return mid_entry->handle(server, mid_entry);
> +               ret = mid_entry->handle(server, mid_entry);
> +       else
> +               ret = cifs_handle_standard(server, mid_entry);

Let's suppose we didn't find a mid for a message in the middle of the
chain. cifs_handle_standard() wan't called and the buffer was't saved
anywhere...

> +
> +       if (ret == 0 && shdr->NextCommand) {
> +               pdu_length -= shdr->NextCommand;
> +               server->large_buf = next_is_large;
> +               if (next_is_large) {
> +                       server->bigbuf = next_buffer;
> +               } else {
> +                       server->smallbuf = next_buffer;
> +               }

...and them we overwrite the server's buffers with the next one in the
chain - leaking a current buffer.

> +               buf += shdr->NextCommand;
> +               goto one_more;
> +       }
>
> -       return cifs_handle_standard(server, mid_entry);
> +        return ret;
>  }
>
>  static int
> -smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
> +smb3_receive_transform(struct TCP_Server_Info *server,
> +                      struct mid_q_entry **mids, int *num_mids)
>  {
>         char *buf = server->smallbuf;
>         unsigned int pdu_length = server->pdu_size;
> @@ -3009,10 +3048,11 @@ smb3_receive_transform(struct TCP_Server_Info *server, struct mid_q_entry **mid)
>                 return -ECONNABORTED;
>         }
>
> +       /* TODO: add support for compounds containing READ. */
>         if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server))
> -               return receive_encrypted_read(server, mid);
> +               return receive_encrypted_read(server, &mids[0]);
>
> -       return receive_encrypted_standard(server, mid);
> +       return receive_encrypted_standard(server, mids, num_mids);
>  }
>
>  int
> diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
> index 92de5c528161..3122c30eaf5c 100644
> --- a/fs/cifs/transport.c
> +++ b/fs/cifs/transport.c
> @@ -378,8 +378,6 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
>         return rc;
>  }
>
> -#define MAX_COMPOUND 5
> -
>  static int
>  smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
>               struct smb_rqst *rqst, int flags)
> --
> 2.13.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

One way to overcome the problems above is to return an array of pairs
{mid; buf} from receive_transform() function and then process them one
by one checking for mid existence, verifying oplock breaks and dumping
responses without corresponding mids (exactly the same way we do it
for mid_entry and buf variables in demultiplex thread today).

--
Best regards,
Pavel Shilovsky
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux