Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: af_alg - avoid undefined behavior accessing salg_name

Commit 3f69cc60768b ("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: 3f69cc60768b ("crypto: af_alg - Allow arbitrarily long algorithm names")
Cc: <stable@vger.kernel.org> # v4.12+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Eric Biggers and committed by
Herbert Xu
92eb6c30 2d653936

+23 -3
+7 -3
crypto/af_alg.c
··· 147 147 const u32 allowed = CRYPTO_ALG_KERN_DRIVER_ONLY; 148 148 struct sock *sk = sock->sk; 149 149 struct alg_sock *ask = alg_sk(sk); 150 - struct sockaddr_alg *sa = (void *)uaddr; 150 + struct sockaddr_alg_new *sa = (void *)uaddr; 151 151 const struct af_alg_type *type; 152 152 void *private; 153 153 int err; ··· 155 155 if (sock->state == SS_CONNECTED) 156 156 return -EINVAL; 157 157 158 - if (addr_len < sizeof(*sa)) 158 + BUILD_BUG_ON(offsetof(struct sockaddr_alg_new, salg_name) != 159 + offsetof(struct sockaddr_alg, salg_name)); 160 + BUILD_BUG_ON(offsetof(struct sockaddr_alg, salg_name) != sizeof(*sa)); 161 + 162 + if (addr_len < sizeof(*sa) + 1) 159 163 return -EINVAL; 160 164 161 165 /* If caller uses non-allowed flag, return error. */ ··· 167 163 return -EINVAL; 168 164 169 165 sa->salg_type[sizeof(sa->salg_type) - 1] = 0; 170 - sa->salg_name[sizeof(sa->salg_name) + addr_len - sizeof(*sa) - 1] = 0; 166 + sa->salg_name[addr_len - sizeof(*sa) - 1] = 0; 171 167 172 168 type = alg_get_type(sa->salg_type); 173 169 if (PTR_ERR(type) == -ENOENT) {
+16
include/uapi/linux/if_alg.h
··· 24 24 __u8 salg_name[64]; 25 25 }; 26 26 27 + /* 28 + * Linux v4.12 and later removed the 64-byte limit on salg_name[]; it's now an 29 + * arbitrary-length field. We had to keep the original struct above for source 30 + * compatibility with existing userspace programs, though. Use the new struct 31 + * below if support for very long algorithm names is needed. To do this, 32 + * allocate 'sizeof(struct sockaddr_alg_new) + strlen(algname) + 1' bytes, and 33 + * copy algname (including the null terminator) into salg_name. 34 + */ 35 + struct sockaddr_alg_new { 36 + __u16 salg_family; 37 + __u8 salg_type[14]; 38 + __u32 salg_feat; 39 + __u32 salg_mask; 40 + __u8 salg_name[]; 41 + }; 42 + 27 43 struct af_alg_iv { 28 44 __u32 ivlen; 29 45 __u8 iv[0];