2009-09-02 14:05:22 +04:00
/*
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
* VMAC : Message Authentication Code using Universal Hashing
*
* Reference : https : //tools.ietf.org/html/draft-krovetz-vmac-01
*
2009-09-02 14:05:22 +04:00
* Copyright ( c ) 2009 , Intel Corporation .
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
* Copyright ( c ) 2018 , Google Inc .
2009-09-02 14:05:22 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
*
* You should have received a copy of the GNU General Public License along with
* this program ; if not , write to the Free Software Foundation , Inc . , 59 Temple
* Place - Suite 330 , Boston , MA 02111 - 1307 USA .
*/
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/*
* Derived from :
* VMAC and VHASH Implementation by Ted Krovetz ( tdk @ acm . org ) and Wei Dai .
* This implementation is herby placed in the public domain .
* The authors offers no warranty . Use at your own risk .
* Last modified : 17 APR 08 , 1700 PDT
*/
2009-09-02 14:05:22 +04:00
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
# include <asm/unaligned.h>
2009-09-02 14:05:22 +04:00
# include <linux/init.h>
# include <linux/types.h>
# include <linux/crypto.h>
2011-05-27 22:41:48 +04:00
# include <linux/module.h>
2009-09-02 14:05:22 +04:00
# include <linux/scatterlist.h>
# include <asm/byteorder.h>
# include <crypto/scatterwalk.h>
# include <crypto/internal/hash.h>
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/*
* User definable settings .
*/
# define VMAC_TAG_LEN 64
# define VMAC_KEY_SIZE 128 /* Must be 128, 192 or 256 */
# define VMAC_KEY_LEN (VMAC_KEY_SIZE / 8)
# define VMAC_NHBYTES 128 /* Must 2^i for any 3 < i < 13 Standard = 128*/
2018-06-18 20:22:39 +03:00
# define VMAC_NONCEBYTES 16
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/* per-transform (per-key) context */
struct vmac_tfm_ctx {
struct crypto_cipher * cipher ;
u64 nhkey [ ( VMAC_NHBYTES / 8 ) + 2 * ( VMAC_TAG_LEN / 64 - 1 ) ] ;
u64 polykey [ 2 * VMAC_TAG_LEN / 64 ] ;
u64 l3key [ 2 * VMAC_TAG_LEN / 64 ] ;
} ;
/* per-request context */
struct vmac_desc_ctx {
union {
u8 partial [ VMAC_NHBYTES ] ; /* partial block */
__le64 partial_words [ VMAC_NHBYTES / 8 ] ;
} ;
unsigned int partial_size ; /* size of the partial block */
bool first_block_processed ;
u64 polytmp [ 2 * VMAC_TAG_LEN / 64 ] ; /* running total of L2-hash */
2018-06-18 20:22:39 +03:00
union {
u8 bytes [ VMAC_NONCEBYTES ] ;
__be64 pads [ VMAC_NONCEBYTES / 8 ] ;
} nonce ;
unsigned int nonce_size ; /* nonce bytes filled so far */
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
} ;
2009-09-02 14:05:22 +04:00
/*
* Constants and masks
*/
# define UINT64_C(x) x##ULL
2012-08-28 17:46:54 +04:00
static const u64 p64 = UINT64_C ( 0xfffffffffffffeff ) ; /* 2^64 - 257 prime */
static const u64 m62 = UINT64_C ( 0x3fffffffffffffff ) ; /* 62-bit mask */
static const u64 m63 = UINT64_C ( 0x7fffffffffffffff ) ; /* 63-bit mask */
static const u64 m64 = UINT64_C ( 0xffffffffffffffff ) ; /* 64-bit mask */
static const u64 mpoly = UINT64_C ( 0x1fffffff1fffffff ) ; /* Poly key mask */
2009-09-02 14:05:22 +04:00
2010-03-18 15:22:55 +03:00
# define pe64_to_cpup le64_to_cpup /* Prefer little endian */
2009-09-02 14:05:22 +04:00
# ifdef __LITTLE_ENDIAN
# define INDEX_HIGH 1
# define INDEX_LOW 0
# else
# define INDEX_HIGH 0
# define INDEX_LOW 1
# endif
/*
* The following routines are used in this implementation . They are
* written via macros to simulate zero - overhead call - by - reference .
*
* MUL64 : 64 x64 - > 128 - bit multiplication
* PMUL64 : assumes top bits cleared on inputs
* ADD128 : 128 x128 - > 128 - bit addition
*/
# define ADD128(rh, rl, ih, il) \
do { \
u64 _il = ( il ) ; \
( rl ) + = ( _il ) ; \
if ( ( rl ) < ( _il ) ) \
( rh ) + + ; \
( rh ) + = ( ih ) ; \
} while ( 0 )
# define MUL32(i1, i2) ((u64)(u32)(i1)*(u32)(i2))
# define PMUL64(rh, rl, i1, i2) /* Assumes m doesn't overflow */ \
do { \
u64 _i1 = ( i1 ) , _i2 = ( i2 ) ; \
u64 m = MUL32 ( _i1 , _i2 > > 32 ) + MUL32 ( _i1 > > 32 , _i2 ) ; \
rh = MUL32 ( _i1 > > 32 , _i2 > > 32 ) ; \
rl = MUL32 ( _i1 , _i2 ) ; \
ADD128 ( rh , rl , ( m > > 32 ) , ( m < < 32 ) ) ; \
} while ( 0 )
# define MUL64(rh, rl, i1, i2) \
do { \
u64 _i1 = ( i1 ) , _i2 = ( i2 ) ; \
u64 m1 = MUL32 ( _i1 , _i2 > > 32 ) ; \
u64 m2 = MUL32 ( _i1 > > 32 , _i2 ) ; \
rh = MUL32 ( _i1 > > 32 , _i2 > > 32 ) ; \
rl = MUL32 ( _i1 , _i2 ) ; \
ADD128 ( rh , rl , ( m1 > > 32 ) , ( m1 < < 32 ) ) ; \
ADD128 ( rh , rl , ( m2 > > 32 ) , ( m2 < < 32 ) ) ; \
} while ( 0 )
/*
* For highest performance the L1 NH and L2 polynomial hashes should be
2011-03-31 05:57:33 +04:00
* carefully implemented to take advantage of one ' s target architecture .
2009-09-02 14:05:22 +04:00
* Here these two hash functions are defined multiple time ; once for
* 64 - bit architectures , once for 32 - bit SSE2 architectures , and once
* for the rest ( 32 - bit ) architectures .
* For each , nh_16 * must * be defined ( works on multiples of 16 bytes ) .
* Optionally , nh_vmac_nhbytes can be defined ( for multiples of
* VMAC_NHBYTES ) , and nh_16_2 and nh_vmac_nhbytes_2 ( versions that do two
* NH computations at once ) .
*/
# ifdef CONFIG_64BIT
# define nh_16(mp, kp, nw, rh, rl) \
do { \
int i ; u64 th , tl ; \
rh = rl = 0 ; \
for ( i = 0 ; i < nw ; i + = 2 ) { \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 1 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
} \
} while ( 0 )
# define nh_16_2(mp, kp, nw, rh, rl, rh1, rl1) \
do { \
int i ; u64 th , tl ; \
rh1 = rl1 = rh = rl = 0 ; \
for ( i = 0 ; i < nw ; i + = 2 ) { \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 1 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i + 2 ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 3 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh1 , rl1 , th , tl ) ; \
} \
} while ( 0 )
# if (VMAC_NHBYTES >= 64) /* These versions do 64-bytes of message at a time */
# define nh_vmac_nhbytes(mp, kp, nw, rh, rl) \
do { \
int i ; u64 th , tl ; \
rh = rl = 0 ; \
for ( i = 0 ; i < nw ; i + = 8 ) { \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 1 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 2 ) + ( kp ) [ i + 2 ] , \
pe64_to_cpup ( ( mp ) + i + 3 ) + ( kp ) [ i + 3 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 4 ) + ( kp ) [ i + 4 ] , \
pe64_to_cpup ( ( mp ) + i + 5 ) + ( kp ) [ i + 5 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 6 ) + ( kp ) [ i + 6 ] , \
pe64_to_cpup ( ( mp ) + i + 7 ) + ( kp ) [ i + 7 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
} \
} while ( 0 )
# define nh_vmac_nhbytes_2(mp, kp, nw, rh, rl, rh1, rl1) \
do { \
int i ; u64 th , tl ; \
rh1 = rl1 = rh = rl = 0 ; \
for ( i = 0 ; i < nw ; i + = 8 ) { \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 1 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i ) + ( kp ) [ i + 2 ] , \
pe64_to_cpup ( ( mp ) + i + 1 ) + ( kp ) [ i + 3 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh1 , rl1 , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 2 ) + ( kp ) [ i + 2 ] , \
pe64_to_cpup ( ( mp ) + i + 3 ) + ( kp ) [ i + 3 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 2 ) + ( kp ) [ i + 4 ] , \
pe64_to_cpup ( ( mp ) + i + 3 ) + ( kp ) [ i + 5 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh1 , rl1 , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 4 ) + ( kp ) [ i + 4 ] , \
pe64_to_cpup ( ( mp ) + i + 5 ) + ( kp ) [ i + 5 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 4 ) + ( kp ) [ i + 6 ] , \
pe64_to_cpup ( ( mp ) + i + 5 ) + ( kp ) [ i + 7 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh1 , rl1 , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 6 ) + ( kp ) [ i + 6 ] , \
pe64_to_cpup ( ( mp ) + i + 7 ) + ( kp ) [ i + 7 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh , rl , th , tl ) ; \
2010-03-18 15:22:55 +03:00
MUL64 ( th , tl , pe64_to_cpup ( ( mp ) + i + 6 ) + ( kp ) [ i + 8 ] , \
pe64_to_cpup ( ( mp ) + i + 7 ) + ( kp ) [ i + 9 ] ) ; \
2009-09-02 14:05:22 +04:00
ADD128 ( rh1 , rl1 , th , tl ) ; \
} \
} while ( 0 )
# endif
# define poly_step(ah, al, kh, kl, mh, ml) \
do { \
u64 t1h , t1l , t2h , t2l , t3h , t3l , z = 0 ; \
/* compute ab*cd, put bd into result registers */ \
PMUL64 ( t3h , t3l , al , kh ) ; \
PMUL64 ( t2h , t2l , ah , kl ) ; \
PMUL64 ( t1h , t1l , ah , 2 * kh ) ; \
PMUL64 ( ah , al , al , kl ) ; \
/* add 2 * ac to result */ \
ADD128 ( ah , al , t1h , t1l ) ; \
/* add together ad + bc */ \
ADD128 ( t2h , t2l , t3h , t3l ) ; \
/* now (ah,al), (t2l,2*t2h) need summing */ \
/* first add the high registers, carrying into t2h */ \
ADD128 ( t2h , ah , z , t2l ) ; \
/* double t2h and add top bit of ah */ \
t2h = 2 * t2h + ( ah > > 63 ) ; \
ah & = m63 ; \
/* now add the low registers */ \
ADD128 ( ah , al , mh , ml ) ; \
ADD128 ( ah , al , z , t2h ) ; \
} while ( 0 )
# else /* ! CONFIG_64BIT */
# ifndef nh_16
# define nh_16(mp, kp, nw, rh, rl) \
do { \
u64 t1 , t2 , m1 , m2 , t ; \
int i ; \
rh = rl = t = 0 ; \
for ( i = 0 ; i < nw ; i + = 2 ) { \
2010-03-18 15:22:55 +03:00
t1 = pe64_to_cpup ( mp + i ) + kp [ i ] ; \
t2 = pe64_to_cpup ( mp + i + 1 ) + kp [ i + 1 ] ; \
2009-09-02 14:05:22 +04:00
m2 = MUL32 ( t1 > > 32 , t2 ) ; \
m1 = MUL32 ( t1 , t2 > > 32 ) ; \
ADD128 ( rh , rl , MUL32 ( t1 > > 32 , t2 > > 32 ) , \
MUL32 ( t1 , t2 ) ) ; \
rh + = ( u64 ) ( u32 ) ( m1 > > 32 ) \
+ ( u32 ) ( m2 > > 32 ) ; \
t + = ( u64 ) ( u32 ) m1 + ( u32 ) m2 ; \
} \
ADD128 ( rh , rl , ( t > > 32 ) , ( t < < 32 ) ) ; \
} while ( 0 )
# endif
static void poly_step_func ( u64 * ahi , u64 * alo ,
const u64 * kh , const u64 * kl ,
const u64 * mh , const u64 * ml )
{
# define a0 (*(((u32 *)alo)+INDEX_LOW))
# define a1 (*(((u32 *)alo)+INDEX_HIGH))
# define a2 (*(((u32 *)ahi)+INDEX_LOW))
# define a3 (*(((u32 *)ahi)+INDEX_HIGH))
# define k0 (*(((u32 *)kl)+INDEX_LOW))
# define k1 (*(((u32 *)kl)+INDEX_HIGH))
# define k2 (*(((u32 *)kh)+INDEX_LOW))
# define k3 (*(((u32 *)kh)+INDEX_HIGH))
u64 p , q , t ;
u32 t2 ;
p = MUL32 ( a3 , k3 ) ;
p + = p ;
p + = * ( u64 * ) mh ;
p + = MUL32 ( a0 , k2 ) ;
p + = MUL32 ( a1 , k1 ) ;
p + = MUL32 ( a2 , k0 ) ;
t = ( u32 ) ( p ) ;
p > > = 32 ;
p + = MUL32 ( a0 , k3 ) ;
p + = MUL32 ( a1 , k2 ) ;
p + = MUL32 ( a2 , k1 ) ;
p + = MUL32 ( a3 , k0 ) ;
t | = ( ( u64 ) ( ( u32 ) p & 0x7fffffff ) ) < < 32 ;
p > > = 31 ;
p + = ( u64 ) ( ( ( u32 * ) ml ) [ INDEX_LOW ] ) ;
p + = MUL32 ( a0 , k0 ) ;
q = MUL32 ( a1 , k3 ) ;
q + = MUL32 ( a2 , k2 ) ;
q + = MUL32 ( a3 , k1 ) ;
q + = q ;
p + = q ;
t2 = ( u32 ) ( p ) ;
p > > = 32 ;
p + = ( u64 ) ( ( ( u32 * ) ml ) [ INDEX_HIGH ] ) ;
p + = MUL32 ( a0 , k1 ) ;
p + = MUL32 ( a1 , k0 ) ;
q = MUL32 ( a2 , k3 ) ;
q + = MUL32 ( a3 , k2 ) ;
q + = q ;
p + = q ;
* ( u64 * ) ( alo ) = ( p < < 32 ) | t2 ;
p > > = 32 ;
* ( u64 * ) ( ahi ) = p + t ;
# undef a0
# undef a1
# undef a2
# undef a3
# undef k0
# undef k1
# undef k2
# undef k3
}
# define poly_step(ah, al, kh, kl, mh, ml) \
poly_step_func ( & ( ah ) , & ( al ) , & ( kh ) , & ( kl ) , & ( mh ) , & ( ml ) )
# endif /* end of specialized NH and poly definitions */
/* At least nh_16 is defined. Defined others as needed here */
# ifndef nh_16_2
# define nh_16_2(mp, kp, nw, rh, rl, rh2, rl2) \
do { \
nh_16 ( mp , kp , nw , rh , rl ) ; \
nh_16 ( mp , ( ( kp ) + 2 ) , nw , rh2 , rl2 ) ; \
} while ( 0 )
# endif
# ifndef nh_vmac_nhbytes
# define nh_vmac_nhbytes(mp, kp, nw, rh, rl) \
nh_16 ( mp , kp , nw , rh , rl )
# endif
# ifndef nh_vmac_nhbytes_2
# define nh_vmac_nhbytes_2(mp, kp, nw, rh, rl, rh2, rl2) \
do { \
nh_vmac_nhbytes ( mp , kp , nw , rh , rl ) ; \
nh_vmac_nhbytes ( mp , ( ( kp ) + 2 ) , nw , rh2 , rl2 ) ; \
} while ( 0 )
# endif
2010-03-18 15:22:55 +03:00
static u64 l3hash ( u64 p1 , u64 p2 , u64 k1 , u64 k2 , u64 len )
2009-09-02 14:05:22 +04:00
{
u64 rh , rl , t , z = 0 ;
/* fully reduce (p1,p2)+(len,0) mod p127 */
t = p1 > > 63 ;
p1 & = m63 ;
ADD128 ( p1 , p2 , len , t ) ;
/* At this point, (p1,p2) is at most 2^127+(len<<64) */
t = ( p1 > m63 ) + ( ( p1 = = m63 ) & & ( p2 = = m64 ) ) ;
ADD128 ( p1 , p2 , z , t ) ;
p1 & = m63 ;
/* compute (p1,p2)/(2^64-2^32) and (p1,p2)%(2^64-2^32) */
t = p1 + ( p2 > > 32 ) ;
t + = ( t > > 32 ) ;
t + = ( u32 ) t > 0xfffffffeu ;
p1 + = ( t > > 32 ) ;
p2 + = ( p1 < < 32 ) ;
/* compute (p1+k1)%p64 and (p2+k2)%p64 */
p1 + = k1 ;
p1 + = ( 0 - ( p1 < k1 ) ) & 257 ;
p2 + = k2 ;
p2 + = ( 0 - ( p2 < k2 ) ) & 257 ;
/* compute (p1+k1)*(p2+k2)%p64 */
MUL64 ( rh , rl , p1 , p2 ) ;
t = rh > > 56 ;
ADD128 ( t , rl , z , rh ) ;
rh < < = 8 ;
ADD128 ( t , rl , z , rh ) ;
t + = t < < 8 ;
rl + = t ;
rl + = ( 0 - ( rl < t ) ) & 257 ;
rl + = ( 0 - ( rl > p64 - 1 ) ) & 257 ;
return rl ;
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/* L1 and L2-hash one or more VMAC_NHBYTES-byte blocks */
static void vhash_blocks ( const struct vmac_tfm_ctx * tctx ,
struct vmac_desc_ctx * dctx ,
const __le64 * mptr , unsigned int blocks )
2009-09-02 14:05:22 +04:00
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
const u64 * kptr = tctx - > nhkey ;
const u64 pkh = tctx - > polykey [ 0 ] ;
const u64 pkl = tctx - > polykey [ 1 ] ;
u64 ch = dctx - > polytmp [ 0 ] ;
u64 cl = dctx - > polytmp [ 1 ] ;
u64 rh , rl ;
if ( ! dctx - > first_block_processed ) {
dctx - > first_block_processed = true ;
2009-09-02 14:05:22 +04:00
nh_vmac_nhbytes ( mptr , kptr , VMAC_NHBYTES / 8 , rh , rl ) ;
rh & = m62 ;
ADD128 ( ch , cl , rh , rl ) ;
mptr + = ( VMAC_NHBYTES / sizeof ( u64 ) ) ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
blocks - - ;
2009-09-02 14:05:22 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
while ( blocks - - ) {
2009-09-02 14:05:22 +04:00
nh_vmac_nhbytes ( mptr , kptr , VMAC_NHBYTES / 8 , rh , rl ) ;
rh & = m62 ;
poly_step ( ch , cl , pkh , pkl , rh , rl ) ;
mptr + = ( VMAC_NHBYTES / sizeof ( u64 ) ) ;
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
dctx - > polytmp [ 0 ] = ch ;
dctx - > polytmp [ 1 ] = cl ;
2009-09-02 14:05:22 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
static int vmac_setkey ( struct crypto_shash * tfm ,
const u8 * key , unsigned int keylen )
2009-09-02 14:05:22 +04:00
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
struct vmac_tfm_ctx * tctx = crypto_shash_ctx ( tfm ) ;
__be64 out [ 2 ] ;
u8 in [ 16 ] = { 0 } ;
unsigned int i ;
int err ;
2009-09-02 14:05:22 +04:00
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
if ( keylen ! = VMAC_KEY_LEN ) {
crypto_shash_set_flags ( tfm , CRYPTO_TFM_RES_BAD_KEY_LEN ) ;
return - EINVAL ;
2009-09-02 14:05:22 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
err = crypto_cipher_setkey ( tctx - > cipher , key , keylen ) ;
2009-09-02 14:05:22 +04:00
if ( err )
return err ;
/* Fill nh key */
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
in [ 0 ] = 0x80 ;
for ( i = 0 ; i < ARRAY_SIZE ( tctx - > nhkey ) ; i + = 2 ) {
crypto_cipher_encrypt_one ( tctx - > cipher , ( u8 * ) out , in ) ;
tctx - > nhkey [ i ] = be64_to_cpu ( out [ 0 ] ) ;
tctx - > nhkey [ i + 1 ] = be64_to_cpu ( out [ 1 ] ) ;
in [ 15 ] + + ;
2009-09-02 14:05:22 +04:00
}
/* Fill poly key */
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
in [ 0 ] = 0xC0 ;
in [ 15 ] = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( tctx - > polykey ) ; i + = 2 ) {
crypto_cipher_encrypt_one ( tctx - > cipher , ( u8 * ) out , in ) ;
tctx - > polykey [ i ] = be64_to_cpu ( out [ 0 ] ) & mpoly ;
tctx - > polykey [ i + 1 ] = be64_to_cpu ( out [ 1 ] ) & mpoly ;
in [ 15 ] + + ;
2009-09-02 14:05:22 +04:00
}
/* Fill ip key */
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
in [ 0 ] = 0xE0 ;
in [ 15 ] = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( tctx - > l3key ) ; i + = 2 ) {
2009-09-02 14:05:22 +04:00
do {
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
crypto_cipher_encrypt_one ( tctx - > cipher , ( u8 * ) out , in ) ;
tctx - > l3key [ i ] = be64_to_cpu ( out [ 0 ] ) ;
tctx - > l3key [ i + 1 ] = be64_to_cpu ( out [ 1 ] ) ;
in [ 15 ] + + ;
} while ( tctx - > l3key [ i ] > = p64 | | tctx - > l3key [ i + 1 ] > = p64 ) ;
2009-09-02 14:05:22 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
return 0 ;
2009-09-02 14:05:22 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
static int vmac_init ( struct shash_desc * desc )
2009-09-02 14:05:22 +04:00
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
const struct vmac_tfm_ctx * tctx = crypto_shash_ctx ( desc - > tfm ) ;
struct vmac_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
2009-09-02 14:05:22 +04:00
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
dctx - > partial_size = 0 ;
dctx - > first_block_processed = false ;
memcpy ( dctx - > polytmp , tctx - > polykey , sizeof ( dctx - > polytmp ) ) ;
2018-06-18 20:22:39 +03:00
dctx - > nonce_size = 0 ;
return 0 ;
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
static int vmac_update ( struct shash_desc * desc , const u8 * p , unsigned int len )
2009-09-02 14:05:22 +04:00
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
const struct vmac_tfm_ctx * tctx = crypto_shash_ctx ( desc - > tfm ) ;
struct vmac_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
unsigned int n ;
2018-06-18 20:22:39 +03:00
/* Nonce is passed as first VMAC_NONCEBYTES bytes of data */
if ( dctx - > nonce_size < VMAC_NONCEBYTES ) {
n = min ( len , VMAC_NONCEBYTES - dctx - > nonce_size ) ;
memcpy ( & dctx - > nonce . bytes [ dctx - > nonce_size ] , p , n ) ;
dctx - > nonce_size + = n ;
p + = n ;
len - = n ;
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
if ( dctx - > partial_size ) {
n = min ( len , VMAC_NHBYTES - dctx - > partial_size ) ;
memcpy ( & dctx - > partial [ dctx - > partial_size ] , p , n ) ;
dctx - > partial_size + = n ;
p + = n ;
len - = n ;
if ( dctx - > partial_size = = VMAC_NHBYTES ) {
vhash_blocks ( tctx , dctx , dctx - > partial_words , 1 ) ;
dctx - > partial_size = 0 ;
}
}
2012-10-06 01:24:14 +04:00
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
if ( len > = VMAC_NHBYTES ) {
n = round_down ( len , VMAC_NHBYTES ) ;
/* TODO: 'p' may be misaligned here */
vhash_blocks ( tctx , dctx , ( const __le64 * ) p , n / VMAC_NHBYTES ) ;
p + = n ;
len - = n ;
2012-10-06 01:24:14 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
if ( len ) {
memcpy ( dctx - > partial , p , len ) ;
dctx - > partial_size = len ;
}
2009-09-02 14:05:22 +04:00
return 0 ;
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
static u64 vhash_final ( const struct vmac_tfm_ctx * tctx ,
struct vmac_desc_ctx * dctx )
2009-09-02 14:05:22 +04:00
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
unsigned int partial = dctx - > partial_size ;
u64 ch = dctx - > polytmp [ 0 ] ;
u64 cl = dctx - > polytmp [ 1 ] ;
/* L1 and L2-hash the final block if needed */
if ( partial ) {
/* Zero-pad to next 128-bit boundary */
unsigned int n = round_up ( partial , 16 ) ;
u64 rh , rl ;
memset ( & dctx - > partial [ partial ] , 0 , n - partial ) ;
nh_16 ( dctx - > partial_words , tctx - > nhkey , n / 8 , rh , rl ) ;
rh & = m62 ;
if ( dctx - > first_block_processed )
poly_step ( ch , cl , tctx - > polykey [ 0 ] , tctx - > polykey [ 1 ] ,
rh , rl ) ;
else
ADD128 ( ch , cl , rh , rl ) ;
2012-10-06 01:24:14 +04:00
}
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/* L3-hash the 128-bit output of L2-hash */
return l3hash ( ch , cl , tctx - > l3key [ 0 ] , tctx - > l3key [ 1 ] , partial * 8 ) ;
}
2018-06-18 20:22:40 +03:00
static int vmac_final ( struct shash_desc * desc , u8 * out )
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
{
const struct vmac_tfm_ctx * tctx = crypto_shash_ctx ( desc - > tfm ) ;
struct vmac_desc_ctx * dctx = shash_desc_ctx ( desc ) ;
int index ;
u64 hash , pad ;
2018-06-18 20:22:39 +03:00
if ( dctx - > nonce_size ! = VMAC_NONCEBYTES )
return - EINVAL ;
/*
* The VMAC specification requires a nonce at least 1 bit shorter than
* the block cipher ' s block length , so we actually only accept a 127 - bit
* nonce . We define the unused bit to be the first one and require that
* it be 0 , so the needed prepending of a 0 bit is implicit .
*/
if ( dctx - > nonce . bytes [ 0 ] & 0x80 )
return - EINVAL ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/* Finish calculating the VHASH of the message */
hash = vhash_final ( tctx , dctx ) ;
/* Generate pseudorandom pad by encrypting the nonce */
2018-06-18 20:22:39 +03:00
BUILD_BUG_ON ( VMAC_NONCEBYTES ! = 2 * ( VMAC_TAG_LEN / 8 ) ) ;
index = dctx - > nonce . bytes [ VMAC_NONCEBYTES - 1 ] & 1 ;
dctx - > nonce . bytes [ VMAC_NONCEBYTES - 1 ] & = ~ 1 ;
crypto_cipher_encrypt_one ( tctx - > cipher , dctx - > nonce . bytes ,
dctx - > nonce . bytes ) ;
pad = be64_to_cpu ( dctx - > nonce . pads [ index ] ) ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
/* The VMAC is the sum of VHASH and the pseudorandom pad */
2018-06-18 20:22:40 +03:00
put_unaligned_be64 ( hash + pad , out ) ;
2009-09-02 14:05:22 +04:00
return 0 ;
}
static int vmac_init_tfm ( struct crypto_tfm * tfm )
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
struct crypto_instance * inst = crypto_tfm_alg_instance ( tfm ) ;
2009-09-02 14:05:22 +04:00
struct crypto_spawn * spawn = crypto_instance_ctx ( inst ) ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
struct vmac_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
struct crypto_cipher * cipher ;
2009-09-02 14:05:22 +04:00
cipher = crypto_spawn_cipher ( spawn ) ;
if ( IS_ERR ( cipher ) )
return PTR_ERR ( cipher ) ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
tctx - > cipher = cipher ;
2009-09-02 14:05:22 +04:00
return 0 ;
}
static void vmac_exit_tfm ( struct crypto_tfm * tfm )
{
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
struct vmac_tfm_ctx * tctx = crypto_tfm_ctx ( tfm ) ;
crypto_free_cipher ( tctx - > cipher ) ;
2009-09-02 14:05:22 +04:00
}
2018-06-18 20:22:40 +03:00
static int vmac_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2009-09-02 14:05:22 +04:00
{
struct shash_instance * inst ;
struct crypto_alg * alg ;
int err ;
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_SHASH ) ;
if ( err )
return err ;
alg = crypto_get_attr_alg ( tb , CRYPTO_ALG_TYPE_CIPHER ,
CRYPTO_ALG_TYPE_MASK ) ;
if ( IS_ERR ( alg ) )
return PTR_ERR ( alg ) ;
2018-06-18 20:22:37 +03:00
err = - EINVAL ;
2018-06-18 20:22:39 +03:00
if ( alg - > cra_blocksize ! = VMAC_NONCEBYTES )
2018-06-18 20:22:37 +03:00
goto out_put_alg ;
2018-06-18 20:22:39 +03:00
inst = shash_alloc_instance ( tmpl - > name , alg ) ;
2009-09-02 14:05:22 +04:00
err = PTR_ERR ( inst ) ;
if ( IS_ERR ( inst ) )
goto out_put_alg ;
err = crypto_init_spawn ( shash_instance_ctx ( inst ) , alg ,
shash_crypto_instance ( inst ) ,
CRYPTO_ALG_TYPE_MASK ) ;
if ( err )
goto out_free_inst ;
inst - > alg . base . cra_priority = alg - > cra_priority ;
inst - > alg . base . cra_blocksize = alg - > cra_blocksize ;
inst - > alg . base . cra_alignmask = alg - > cra_alignmask ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
inst - > alg . base . cra_ctxsize = sizeof ( struct vmac_tfm_ctx ) ;
2009-09-02 14:05:22 +04:00
inst - > alg . base . cra_init = vmac_init_tfm ;
inst - > alg . base . cra_exit = vmac_exit_tfm ;
crypto: vmac - separate tfm and request context
syzbot reported a crash in vmac_final() when multiple threads
concurrently use the same "vmac(aes)" transform through AF_ALG. The bug
is pretty fundamental: the VMAC template doesn't separate per-request
state from per-tfm (per-key) state like the other hash algorithms do,
but rather stores it all in the tfm context. That's wrong.
Also, vmac_final() incorrectly zeroes most of the state including the
derived keys and cached pseudorandom pad. Therefore, only the first
VMAC invocation with a given key calculates the correct digest.
Fix these bugs by splitting the per-tfm state from the per-request state
and using the proper init/update/final sequencing for requests.
Reproducer for the crash:
#include <linux/if_alg.h>
#include <sys/socket.h>
#include <unistd.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "hash",
.salg_name = "vmac(aes)",
};
char buf[256] = { 0 };
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, buf, 16);
fork();
fd = accept(fd, NULL, NULL);
for (;;)
write(fd, buf, 256);
}
The immediate cause of the crash is that vmac_ctx_t.partial_size exceeds
VMAC_NHBYTES, causing vmac_final() to memset() a negative length.
Reported-by: syzbot+264bca3a6e8d645550d3@syzkaller.appspotmail.com
Fixes: f1939f7c5645 ("crypto: vmac - New hash algorithm for intel_txt support")
Cc: <stable@vger.kernel.org> # v2.6.32+
Signed-off-by: Eric Biggers <ebiggers@google.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2018-06-18 20:22:38 +03:00
inst - > alg . descsize = sizeof ( struct vmac_desc_ctx ) ;
inst - > alg . digestsize = VMAC_TAG_LEN / 8 ;
2018-06-18 20:22:40 +03:00
inst - > alg . init = vmac_init ;
2009-09-02 14:05:22 +04:00
inst - > alg . update = vmac_update ;
2018-06-18 20:22:40 +03:00
inst - > alg . final = vmac_final ;
2009-09-02 14:05:22 +04:00
inst - > alg . setkey = vmac_setkey ;
err = shash_register_instance ( tmpl , inst ) ;
if ( err ) {
out_free_inst :
shash_free_instance ( shash_crypto_instance ( inst ) ) ;
}
out_put_alg :
crypto_mod_put ( alg ) ;
return err ;
}
2018-06-18 20:22:39 +03:00
static struct crypto_template vmac64_tmpl = {
. name = " vmac64 " ,
2018-06-18 20:22:40 +03:00
. create = vmac_create ,
2018-06-18 20:22:39 +03:00
. free = shash_free_instance ,
. module = THIS_MODULE ,
} ;
2009-09-02 14:05:22 +04:00
static int __init vmac_module_init ( void )
{
2018-06-18 20:22:40 +03:00
return crypto_register_template ( & vmac64_tmpl ) ;
2009-09-02 14:05:22 +04:00
}
static void __exit vmac_module_exit ( void )
{
2018-06-18 20:22:39 +03:00
crypto_unregister_template ( & vmac64_tmpl ) ;
2009-09-02 14:05:22 +04:00
}
module_init ( vmac_module_init ) ;
module_exit ( vmac_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " VMAC hash algorithm " ) ;
2018-06-18 20:22:39 +03:00
MODULE_ALIAS_CRYPTO ( " vmac64 " ) ;