Commit 92eb6c30 authored by Eric Biggers's avatar Eric Biggers Committed by Herbert Xu

crypto: af_alg - avoid undefined behavior accessing salg_name

Commit 3f69cc60 ("crypto: af_alg - Allow arbitrarily long algorithm
names") made the kernel start accepting arbitrarily long algorithm names
in sockaddr_alg.  However, the actual length of the salg_name field
stayed at the original 64 bytes.

This is broken because the kernel can access indices >= 64 in salg_name,
which is undefined behavior -- even though the memory that is accessed
is still located within the sockaddr structure.  It would only be
defined behavior if the array were properly marked as arbitrary-length
(either by making it a flexible array, which is the recommended way
these days, or by making it an array of length 0 or 1).

We can't simply change salg_name into a flexible array, since that would
break source compatibility with userspace programs that embed
sockaddr_alg into another struct, or (more commonly) declare a
sockaddr_alg like 'struct sockaddr_alg sa = { .salg_name = "foo" };'.

One solution would be to change salg_name into a flexible array only
when '#ifdef __KERNEL__'.  However, that would keep userspace without an
easy way to actually use the longer algorithm names.

Instead, add a new structure 'sockaddr_alg_new' that has the flexible
array field, and expose it to both userspace and the kernel.
Make the kernel use it correctly in alg_bind().

This addresses the syzbot report
"UBSAN: array-index-out-of-bounds in alg_bind"
(https://syzkaller.appspot.com/bug?extid=92ead4eb8e26a26d465e).

Reported-by: syzbot+92ead4eb8e26a26d465e@syzkaller.appspotmail.com
Fixes: 3f69cc60 ("crypto: af_alg - Allow arbitrarily long algorithm names")
Cc: <stable@vger.kernel.org> # v4.12+
Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
parent 2d653936
...@@ -147,7 +147,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -147,7 +147,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY; const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY;
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct alg_sock *ask = alg_sk(sk); struct alg_sock *ask = alg_sk(sk);
struct sockaddr_alg *sa = (void *)uaddr; struct sockaddr_alg_new *sa = (void *)uaddr;
const struct af_alg_type *type; const struct af_alg_type *type;
void *private; void *private;
int err; int err;
...@@ -155,7 +155,11 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -155,7 +155,11 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (sock->state == SS_CONNECTED) if (sock->state == SS_CONNECTED)
return -EINVAL; return -EINVAL;
if (addr_len < sizeof(*sa)) BUILD_BUG_ON(offsetof(struct sockaddr_alg_new, salg_name) !=
offsetof(struct sockaddr_alg, salg_name));
BUILD_BUG_ON(offsetof(struct sockaddr_alg, salg_name) != sizeof(*sa));
if (addr_len < sizeof(*sa) + 1)
return -EINVAL; return -EINVAL;
/* If caller uses non-allowed flag, return error. */ /* If caller uses non-allowed flag, return error. */
...@@ -163,7 +167,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -163,7 +167,7 @@ static int alg_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL; return -EINVAL;
sa->salg_type[sizeof(sa->salg_type) - 1] = 0; sa->salg_type[sizeof(sa->salg_type) - 1] = 0;
sa->salg_name[sizeof(sa->salg_name) + addr_len - sizeof(*sa) - 1] = 0; sa->salg_name[addr_len - sizeof(*sa) - 1] = 0;
type = alg_get_type(sa->salg_type); type = alg_get_type(sa->salg_type);
if (PTR_ERR(type) == -ENOENT) { if (PTR_ERR(type) == -ENOENT) {
......
...@@ -24,6 +24,22 @@ struct sockaddr_alg { ...@@ -24,6 +24,22 @@ struct sockaddr_alg {
__u8 salg_name[64]; __u8 salg_name[64];
}; };
/*
* Linux v4.12 and later removed the 64-byte limit on salg_name[]; it's now an
* arbitrary-length field. We had to keep the original struct above for source
* compatibility with existing userspace programs, though. Use the new struct
* below if support for very long algorithm names is needed. To do this,
* allocate 'sizeof(struct sockaddr_alg_new) + strlen(algname) + 1' bytes, and
* copy algname (including the null terminator) into salg_name.
*/
struct sockaddr_alg_new {
__u16 salg_family;
__u8 salg_type[14];
__u32 salg_feat;
__u32 salg_mask;
__u8 salg_name[];
};
struct af_alg_iv { struct af_alg_iv {
__u32 ivlen; __u32 ivlen;
__u8 iv[0]; __u8 iv[0];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment