Re: [v2 PATCH] crypto: skcipher - Unmap pages after an external error

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

 



On Thu, 5 Sep 2019 at 20:13, Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx> wrote:
>
> skcipher_walk_done may be called with an error by internal or
> external callers.  For those internal callers we shouldn't unmap
> pages but for external callers we must unmap any pages that are
> in use.
>
> This patch distinguishes between the two cases by checking whether
> walk->nbytes is zero or not.  For internal callers, we now set
> walk->nbytes to zero prior to the call.  For external callers,
> walk->nbytes has always been non-zero (as zero is used to indicate
> the termination of a walk).
>
> Reported-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx>
> Fixes: 5cde0af2a982 ("[CRYPTO] cipher: Added block cipher type")
> Cc: <stable@xxxxxxxxxxxxxxx>
> Signed-off-by: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
>
> diff --git a/crypto/skcipher.c b/crypto/skcipher.c
> index 5d836fc3df3e..22753c1c7202 100644
> --- a/crypto/skcipher.c
> +++ b/crypto/skcipher.c
> @@ -90,7 +90,7 @@ static inline u8 *skcipher_get_spot(u8 *start, unsigned int len)
>         return max(start, end_page);
>  }
>
> -static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
> +static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
>  {
>         u8 *addr;
>
> @@ -98,19 +98,21 @@ static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
>         addr = skcipher_get_spot(addr, bsize);
>         scatterwalk_copychunks(addr, &walk->out, bsize,
>                                (walk->flags & SKCIPHER_WALK_PHYS) ? 2 : 1);
> +       return 0;
>  }
>
>  int skcipher_walk_done(struct skcipher_walk *walk, int err)
>  {
> -       unsigned int n; /* bytes processed */
> -       bool more;
> +       unsigned int n = walk->nbytes;
> +       unsigned int nbytes = 0;
>
> -       if (unlikely(err < 0))
> +       if (!n)
>                 goto finish;
>
> -       n = walk->nbytes - err;
> -       walk->total -= n;
> -       more = (walk->total != 0);
> +       if (likely(err >= 0)) {
> +               n -= err;
> +               nbytes = walk->total - n;
> +       }
>
>         if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
>                                     SKCIPHER_WALK_SLOW |

With this change, we still copy out the output in the
SKCIPHER_WALK_COPY or SKCIPHER_WALK_SLOW cases. I'd expect the failure
case to only do the kunmap()s, but otherwise not make any changes that
are visible to the caller.


> @@ -126,7 +128,7 @@ int skcipher_walk_done(struct skcipher_walk *walk, int err)
>                 memcpy(walk->dst.virt.addr, walk->page, n);
>                 skcipher_unmap_dst(walk);
>         } else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) {
> -               if (err) {
> +               if (err > 0) {
>                         /*
>                          * Didn't process all bytes.  Either the algorithm is
>                          * broken, or this was the last step and it turned out
> @@ -134,27 +136,29 @@ int skcipher_walk_done(struct skcipher_walk *walk, int err)
>                          * the algorithm requires it.
>                          */
>                         err = -EINVAL;
> -                       goto finish;
> -               }
> -               skcipher_done_slow(walk, n);
> -               goto already_advanced;
> +                       nbytes = 0;
> +               } else
> +                       n = skcipher_done_slow(walk, n);
>         }
>
> +       if (err > 0)
> +               err = 0;
> +
> +       walk->total = nbytes;
> +       walk->nbytes = 0;
> +
>         scatterwalk_advance(&walk->in, n);
>         scatterwalk_advance(&walk->out, n);
> -already_advanced:
> -       scatterwalk_done(&walk->in, 0, more);
> -       scatterwalk_done(&walk->out, 1, more);
> +       scatterwalk_done(&walk->in, 0, nbytes);
> +       scatterwalk_done(&walk->out, 1, nbytes);
>
> -       if (more) {
> +       if (nbytes) {
>                 crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ?
>                              CRYPTO_TFM_REQ_MAY_SLEEP : 0);
>                 return skcipher_walk_next(walk);
>         }
> -       err = 0;
> -finish:
> -       walk->nbytes = 0;
>
> +finish:
>         /* Short-circuit for the common/fast path. */
>         if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
>                 goto out;
> --
> Email: Herbert Xu <herbert@xxxxxxxxxxxxxxxxxxx>
> Home Page: http://gondor.apana.org.au/~herbert/
> PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux