cifs: fix kref underflow in close_shroot()
diff mbox series

Message ID 20190327235742.6236-1-lsahlber@redhat.com
State New
Headers show
Series
  • cifs: fix kref underflow in close_shroot()
Related show

Commit Message

Ronnie Sahlberg March 27, 2019, 11:57 p.m. UTC
Fix a bug where we used to not initialize the cached fid structure at all
in open_shroot() if the open was successful but we did not get a lease.
This would leave the structure uninitialized and later when we close the handle
we would in close_shroot() try to kref_put() an uninitialized refcount.

Fix this by always initializing this structure when we are about to return
0/success but make the extra kref_get() on the refcount conditional to
whether we got a lease or not.
This extra get() is only used to hold the structure until we get a lease
break from the server at which point we will kref_put() it during lease
processing.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/smb2ops.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

Comments

Pavel Shilovsky March 28, 2019, 12:17 a.m. UTC | #1
ср, 27 мар. 2019 г. в 16:58, Ronnie Sahlberg <lsahlber@redhat.com>:
>
> Fix a bug where we used to not initialize the cached fid structure at all
> in open_shroot() if the open was successful but we did not get a lease.
> This would leave the structure uninitialized and later when we close the handle
> we would in close_shroot() try to kref_put() an uninitialized refcount.
>
> Fix this by always initializing this structure when we are about to return
> 0/success but make the extra kref_get() on the refcount conditional to
> whether we got a lease or not.
> This extra get() is only used to hold the structure until we get a lease
> break from the server at which point we will kref_put() it during lease
> processing.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/smb2ops.c | 22 ++++++++++++++--------
>  1 file changed, 14 insertions(+), 8 deletions(-)
>
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index 1022a3771e14..4f0fb53cd0de 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -724,14 +724,6 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
>         else
>                 goto oshr_exit;
>
> -
> -       memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
> -       tcon->crfid.tcon = tcon;
> -       tcon->crfid.is_valid = true;
> -       kref_init(&tcon->crfid.refcount);
> -       kref_get(&tcon->crfid.refcount);
> -
> -
>         qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
>         if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
>                 goto oshr_exit;
> @@ -745,6 +737,20 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
>         tcon->crfid.file_all_info_is_valid = 1;
>
>   oshr_exit:
> +       /*
> +        * If we return 0/success then we must also initialize this struct.
> +        * If we got a lease we need to take out an extra get() to keep
> +        * the structure around until we receive the actual lease break.
> +        */
> +       if (rc == 0) {
            ^^^
This will still leak a handle if smb2_validate_and_copy_iov failed above.

--
Best regards,
Pavel Shilovsky
ronnie sahlberg March 28, 2019, 1:22 a.m. UTC | #2
On Thu, Mar 28, 2019 at 10:17 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>
> ср, 27 мар. 2019 г. в 16:58, Ronnie Sahlberg <lsahlber@redhat.com>:
> >
> > Fix a bug where we used to not initialize the cached fid structure at all
> > in open_shroot() if the open was successful but we did not get a lease.
> > This would leave the structure uninitialized and later when we close the handle
> > we would in close_shroot() try to kref_put() an uninitialized refcount.
> >
> > Fix this by always initializing this structure when we are about to return
> > 0/success but make the extra kref_get() on the refcount conditional to
> > whether we got a lease or not.
> > This extra get() is only used to hold the structure until we get a lease
> > break from the server at which point we will kref_put() it during lease
> > processing.
> >
> > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > ---
> >  fs/cifs/smb2ops.c | 22 ++++++++++++++--------
> >  1 file changed, 14 insertions(+), 8 deletions(-)
> >
> > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> > index 1022a3771e14..4f0fb53cd0de 100644
> > --- a/fs/cifs/smb2ops.c
> > +++ b/fs/cifs/smb2ops.c
> > @@ -724,14 +724,6 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
> >         else
> >                 goto oshr_exit;
> >
> > -
> > -       memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
> > -       tcon->crfid.tcon = tcon;
> > -       tcon->crfid.is_valid = true;
> > -       kref_init(&tcon->crfid.refcount);
> > -       kref_get(&tcon->crfid.refcount);
> > -
> > -
> >         qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
> >         if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
> >                 goto oshr_exit;
> > @@ -745,6 +737,20 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
> >         tcon->crfid.file_all_info_is_valid = 1;
> >
> >   oshr_exit:
> > +       /*
> > +        * If we return 0/success then we must also initialize this struct.
> > +        * If we got a lease we need to take out an extra get() to keep
> > +        * the structure around until we receive the actual lease break.
> > +        */
> > +       if (rc == 0) {
>             ^^^
> This will still leak a handle if smb2_validate_and_copy_iov failed above.

Right. I will resend.
>
> --
> Best regards,
> Pavel Shilovsky

Patch
diff mbox series

diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 1022a3771e14..4f0fb53cd0de 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -724,14 +724,6 @@  int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
 	else
 		goto oshr_exit;
 
-
-	memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
-	tcon->crfid.tcon = tcon;
-	tcon->crfid.is_valid = true;
-	kref_init(&tcon->crfid.refcount);
-	kref_get(&tcon->crfid.refcount);
-
-
 	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
 	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
 		goto oshr_exit;
@@ -745,6 +737,20 @@  int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
 	tcon->crfid.file_all_info_is_valid = 1;
 
  oshr_exit:
+	/*
+	 * If we return 0/success then we must also initialize this struct.
+	 * If we got a lease we need to take out an extra get() to keep
+	 * the structure around until we receive the actual lease break.
+	 */
+	if (rc == 0) {
+		memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
+		tcon->crfid.tcon = tcon;
+		tcon->crfid.is_valid = true;
+		kref_init(&tcon->crfid.refcount);
+		if (o_rsp && o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
+			kref_get(&tcon->crfid.refcount);
+	}
+
 	mutex_unlock(&tcon->crfid.fid_mutex);
 	SMB2_open_free(&rqst[0]);
 	SMB2_query_info_free(&rqst[1]);