summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2019-09-05 23:13:06 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2019-09-09 03:35:27 -0400
commit0ba3c026e685573bd3534c17e27da7c505ac99c4 (patch)
tree62a5c9b06044d73c4b004c2000ec87b24c319a36
parent7b865ec15ed3a1a4204537bc28bbc68202f4c52f (diff)
crypto: skcipher - Unmap pages after an external error
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@linaro.org> Fixes: 5cde0af2a982 ("[CRYPTO] cipher: Added block cipher type") Cc: <stable@vger.kernel.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r--crypto/skcipher.c42
1 files changed, 23 insertions, 19 deletions
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)
90 return max(start, end_page); 90 return max(start, end_page);
91} 91}
92 92
93static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize) 93static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
94{ 94{
95 u8 *addr; 95 u8 *addr;
96 96
@@ -98,19 +98,21 @@ static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
98 addr = skcipher_get_spot(addr, bsize); 98 addr = skcipher_get_spot(addr, bsize);
99 scatterwalk_copychunks(addr, &walk->out, bsize, 99 scatterwalk_copychunks(addr, &walk->out, bsize,
100 (walk->flags & SKCIPHER_WALK_PHYS) ? 2 : 1); 100 (walk->flags & SKCIPHER_WALK_PHYS) ? 2 : 1);
101 return 0;
101} 102}
102 103
103int skcipher_walk_done(struct skcipher_walk *walk, int err) 104int skcipher_walk_done(struct skcipher_walk *walk, int err)
104{ 105{
105 unsigned int n; /* bytes processed */ 106 unsigned int n = walk->nbytes;
106 bool more; 107 unsigned int nbytes = 0;
107 108
108 if (unlikely(err < 0)) 109 if (!n)
109 goto finish; 110 goto finish;
110 111
111 n = walk->nbytes - err; 112 if (likely(err >= 0)) {
112 walk->total -= n; 113 n -= err;
113 more = (walk->total != 0); 114 nbytes = walk->total - n;
115 }
114 116
115 if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS | 117 if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
116 SKCIPHER_WALK_SLOW | 118 SKCIPHER_WALK_SLOW |
@@ -126,7 +128,7 @@ unmap_src:
126 memcpy(walk->dst.virt.addr, walk->page, n); 128 memcpy(walk->dst.virt.addr, walk->page, n);
127 skcipher_unmap_dst(walk); 129 skcipher_unmap_dst(walk);
128 } else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) { 130 } else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) {
129 if (err) { 131 if (err > 0) {
130 /* 132 /*
131 * Didn't process all bytes. Either the algorithm is 133 * Didn't process all bytes. Either the algorithm is
132 * broken, or this was the last step and it turned out 134 * broken, or this was the last step and it turned out
@@ -134,27 +136,29 @@ unmap_src:
134 * the algorithm requires it. 136 * the algorithm requires it.
135 */ 137 */
136 err = -EINVAL; 138 err = -EINVAL;
137 goto finish; 139 nbytes = 0;
138 } 140 } else
139 skcipher_done_slow(walk, n); 141 n = skcipher_done_slow(walk, n);
140 goto already_advanced;
141 } 142 }
142 143
144 if (err > 0)
145 err = 0;
146
147 walk->total = nbytes;
148 walk->nbytes = 0;
149
143 scatterwalk_advance(&walk->in, n); 150 scatterwalk_advance(&walk->in, n);
144 scatterwalk_advance(&walk->out, n); 151 scatterwalk_advance(&walk->out, n);
145already_advanced: 152 scatterwalk_done(&walk->in, 0, nbytes);
146 scatterwalk_done(&walk->in, 0, more); 153 scatterwalk_done(&walk->out, 1, nbytes);
147 scatterwalk_done(&walk->out, 1, more);
148 154
149 if (more) { 155 if (nbytes) {
150 crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ? 156 crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ?
151 CRYPTO_TFM_REQ_MAY_SLEEP : 0); 157 CRYPTO_TFM_REQ_MAY_SLEEP : 0);
152 return skcipher_walk_next(walk); 158 return skcipher_walk_next(walk);
153 } 159 }
154 err = 0;
155finish:
156 walk->nbytes = 0;
157 160
161finish:
158 /* Short-circuit for the common/fast path. */ 162 /* Short-circuit for the common/fast path. */
159 if (!((unsigned long)walk->buffer | (unsigned long)walk->page)) 163 if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
160 goto out; 164 goto out;