Skip to content

Commit

Permalink
crypto: skcipher - fix crash flushing dcache in error path
Browse files Browse the repository at this point in the history
scatterwalk_done() is only meant to be called after a nonzero number of
bytes have been processed, since scatterwalk_pagedone() will flush the
dcache of the *previous* page.  But in the error case of
skcipher_walk_done(), e.g. if the input wasn't an integer number of
blocks, scatterwalk_done() was actually called after advancing 0 bytes.
This caused a crash ("BUG: unable to handle kernel paging request")
during '!PageSlab(page)' on architectures like arm and arm64 that define
ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE, provided that the input was
page-aligned as in that case walk->offset == 0.

Fix it by reorganizing skcipher_walk_done() to skip the
scatterwalk_advance() and scatterwalk_done() if an error has occurred.

This bug was found by syzkaller fuzzing.

Reproducer, assuming ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE:

	#include <linux/if_alg.h>
	#include <sys/socket.h>
	#include <unistd.h>

	int main()
	{
		struct sockaddr_alg addr = {
			.salg_type = "skcipher",
			.salg_name = "cbc(aes-generic)",
		};
		char buffer[4096] __attribute__((aligned(4096))) = { 0 };
		int fd;

		fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
		bind(fd, (void *)&addr, sizeof(addr));
		setsockopt(fd, SOL_ALG, ALG_SET_KEY, buffer, 16);
		fd = accept(fd, NULL, NULL);
		write(fd, buffer, 15);
		read(fd, buffer, 15);
	}

Reported-by: Liu Chao <[email protected]>
Fixes: b286d8b ("crypto: skcipher - Add skcipher walk interface")
Cc: <[email protected]> # v4.10+
Signed-off-by: Eric Biggers <[email protected]>
Signed-off-by: Herbert Xu <[email protected]>
  • Loading branch information
ebiggers authored and herbertx committed Aug 3, 2018
1 parent 2a57c0b commit 8088d3d
Showing 1 changed file with 27 additions and 26 deletions.
53 changes: 27 additions & 26 deletions crypto/skcipher.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,31 +95,32 @@ static inline u8 *skcipher_get_spot(u8 *start, unsigned int len)
return max(start, end_page);
}

static int skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
static void skcipher_done_slow(struct skcipher_walk *walk, unsigned int bsize)
{
u8 *addr;

addr = (u8 *)ALIGN((unsigned long)walk->buffer, walk->alignmask + 1);
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 = walk->nbytes - err;
unsigned int nbytes;

nbytes = walk->total - n;

if (unlikely(err < 0)) {
nbytes = 0;
n = 0;
} else if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
SKCIPHER_WALK_SLOW |
SKCIPHER_WALK_COPY |
SKCIPHER_WALK_DIFF)))) {
unsigned int n; /* bytes processed */
bool more;

if (unlikely(err < 0))
goto finish;

n = walk->nbytes - err;
walk->total -= n;
more = (walk->total != 0);

if (likely(!(walk->flags & (SKCIPHER_WALK_PHYS |
SKCIPHER_WALK_SLOW |
SKCIPHER_WALK_COPY |
SKCIPHER_WALK_DIFF)))) {
unmap_src:
skcipher_unmap_src(walk);
} else if (walk->flags & SKCIPHER_WALK_DIFF) {
Expand All @@ -131,28 +132,28 @@ int skcipher_walk_done(struct skcipher_walk *walk, int err)
skcipher_unmap_dst(walk);
} else if (unlikely(walk->flags & SKCIPHER_WALK_SLOW)) {
if (WARN_ON(err)) {
/* unexpected case; didn't process all bytes */
err = -EINVAL;
nbytes = 0;
} else
n = skcipher_done_slow(walk, n);
goto finish;
}
skcipher_done_slow(walk, n);
goto already_advanced;
}

if (err > 0)
err = 0;

walk->total = nbytes;
walk->nbytes = nbytes;

scatterwalk_advance(&walk->in, n);
scatterwalk_advance(&walk->out, n);
scatterwalk_done(&walk->in, 0, nbytes);
scatterwalk_done(&walk->out, 1, nbytes);
already_advanced:
scatterwalk_done(&walk->in, 0, more);
scatterwalk_done(&walk->out, 1, more);

if (nbytes) {
if (more) {
crypto_yield(walk->flags & SKCIPHER_WALK_SLEEP ?
CRYPTO_TFM_REQ_MAY_SLEEP : 0);
return skcipher_walk_next(walk);
}
err = 0;
finish:
walk->nbytes = 0;

/* Short-circuit for the common/fast path. */
if (!((unsigned long)walk->buffer | (unsigned long)walk->page))
Expand Down

0 comments on commit 8088d3d

Please sign in to comment.