[Dovecot] Bug in dovecot 2.2.5: segfault due to bad alignment
Take a look at the sources, hmac.h declares struct hmac_context:
struct hmac_context { char ctx[HMAC_MAX_CONTEXT_SIZE]; char ctxo[HMAC_MAX_CONTEXT_SIZE]; const struct hash_method *hash; };
If compiled for a 32 bit virtual address space, this has an alignment requirement of 4 due to the hash pointer.
In line 171 of auth-token.c, we have following declaration of ctx as a local variable in auth_token_get():
struct hmac_context ctx;
This is put on an address with an alignment requirement of 4. In lines 174 and 175 hmac_init is invoked with hash_method_sha1:
hmac_init(&ctx, (const unsigned char*)username, strlen(username), &hash_method_sha1);
In hmac.c, lines 43 and following, ctx->ctx with an alignment of 4 is passed to meth->init and meth->loop where meth refers to hash_method_sha1:
meth->init(ctx->ctx); meth->loop(ctx->ctx, k_ipad, 64);
These functions refer now to sha1_init and sha1_loop where the first parameter is expected to be a pointer to struct sha1_ctxt, a data structure which is declared in sha1.h:
struct sha1_ctxt { union { uint8_t b8[20]; uint32_t b32[5]; } h; union { uint8_t b8[8]; uint64_t b64[1]; } c; union { uint8_t b8[64]; uint32_t b32[16]; } m; uint8_t count; };
Here we have with b64 one uint64_t which has on a SPARC platform an alignment requirement of 8. In consequence, struct sha1_ctxt has an alignment requirement of 8. With the invocations of meth->init and meth->loop above we pass a pointer to a data structure of alignment 4 to a function expecting a pointer to a data structure of alignment 8. Chances are that the alignment requirement is not met, causing a segmentation violation.
This must be solved by declaring struct hmac_context such that is not just big enough but respects also the highest alignment required for one of the hashing data structures.
There are several options to do this:
Beginning with C11, you are free to use an alignment specifier, i.e. add _Alignas ( uint64_t ) (see section 6.7.5 in ISO 9899-2011)
GCC supports alignment attributes, i.e. add __attribute__ ((aligned (8))) or whatever is required instead of 8, see http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
Do not use a local variable for it, allocate the data structure using malloc instead.
If you want to see a live crash, here is the relevant output of gdb that debugs ${prefix}/libexec/dovecot/auth.
Program received signal SIGSEGV, Segmentation fault.
sha1_loop (ctxt=0xffbff63c, input=0xffbff548, len=64) at sha1.c:224
224 sha1.c: No such file or directory.
(gdb) where
#0 sha1_loop (ctxt=0xffbff63c, input=0xffbff548, len=64) at sha1.c:224
#1 0xff2e218c in hmac_init (ctx=ctx@entry=0xffbff63c,
key=key@entry=0x6a698 "borchert", key_len=8,
meth=0x555d0
As you can see, ctxt is on a 4-byte boundary, not on an 8-byte boundary. The crash happens at sha1.c:224 where the 8-byte-alignment is indeed mandatory on a SPARC architecture:
ctxt->c.b64[0] += copysiz * 8;
The environment is Solaris 10 on SPARCv9. The sources have been compiled using gcc 4.8.0 for 32 bit.
Andreas.
Attached patch fixes this? If yes, I'll commit it.
On 21.8.2013, at 19.32, Andreas F. Borchert dovecot@andreas-borchert.de wrote:
Take a look at the sources, hmac.h declares struct hmac_context:
struct hmac_context { char ctx[HMAC_MAX_CONTEXT_SIZE]; char ctxo[HMAC_MAX_CONTEXT_SIZE]; const struct hash_method *hash; };
If compiled for a 32 bit virtual address space, this has an alignment requirement of 4 due to the hash pointer.
In line 171 of auth-token.c, we have following declaration of ctx as a local variable in auth_token_get():
struct hmac_context ctx;
This is put on an address with an alignment requirement of 4. In lines 174 and 175 hmac_init is invoked with hash_method_sha1:
hmac_init(&ctx, (const unsigned char*)username, strlen(username), &hash_method_sha1);
In hmac.c, lines 43 and following, ctx->ctx with an alignment of 4 is passed to meth->init and meth->loop where meth refers to hash_method_sha1:
meth->init(ctx->ctx); meth->loop(ctx->ctx, k_ipad, 64);
These functions refer now to sha1_init and sha1_loop where the first parameter is expected to be a pointer to struct sha1_ctxt, a data structure which is declared in sha1.h:
struct sha1_ctxt { union { uint8_t b8[20]; uint32_t b32[5]; } h; union { uint8_t b8[8]; uint64_t b64[1]; } c; union { uint8_t b8[64]; uint32_t b32[16]; } m; uint8_t count; };
Here we have with b64 one uint64_t which has on a SPARC platform an alignment requirement of 8. In consequence, struct sha1_ctxt has an alignment requirement of 8. With the invocations of meth->init and meth->loop above we pass a pointer to a data structure of alignment 4 to a function expecting a pointer to a data structure of alignment 8. Chances are that the alignment requirement is not met, causing a segmentation violation.
This must be solved by declaring struct hmac_context such that is not just big enough but respects also the highest alignment required for one of the hashing data structures.
There are several options to do this:
Beginning with C11, you are free to use an alignment specifier, i.e. add _Alignas ( uint64_t ) (see section 6.7.5 in ISO 9899-2011)
GCC supports alignment attributes, i.e. add __attribute__ ((aligned (8))) or whatever is required instead of 8, see http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
Do not use a local variable for it, allocate the data structure using malloc instead.
If you want to see a live crash, here is the relevant output of gdb that debugs ${prefix}/libexec/dovecot/auth.
Program received signal SIGSEGV, Segmentation fault. sha1_loop (ctxt=0xffbff63c, input=0xffbff548, len=64) at sha1.c:224 224 sha1.c: No such file or directory. (gdb) where #0 sha1_loop (ctxt=0xffbff63c, input=0xffbff548, len=64) at sha1.c:224 #1 0xff2e218c in hmac_init (ctx=ctx@entry=0xffbff63c, key=key@entry=0x6a698 "borchert", key_len=8, meth=0x555d0
) at hmac.c:44 #2 0x00023310 in auth_token_get (service=service@entry=0x6a648 "imap", session_pid=0x56071 "26272", username=0x6a698 "borchert", session_id=0x6a650 "AErv6nbk6gB/AAAB") at auth-token.c:174 #3 0x00021708 in userdb_callback (result=USERDB_RESULT_OK, request=0x6a530) at auth-request-handler.c:668 #4 0x0001f144 in auth_request_userdb_callback (result=<optimized out>, result@entry=USERDB_RESULT_OK, request=request@entry=0x6a530) at auth-request.c:1039 #5 0x000312c8 in prefetch_lookup (auth_request=0x6a530, callback=0x1f058 ) at userdb-prefetch.c:40 #6 0x0001f37c in auth_request_lookup_user (request=0x6a530, callback=callback@entry=0x2150c ) at auth-request.c:1072 #7 0x00022034 in auth_request_handler_master_request ( handler=<optimized out>, master=master@entry=0x6b120, id=1292369921, client_id=1, params=0x55bfc) at auth-request-handler.c:758 #8 0x0001be98 in master_input_request (args=<optimized out>, conn=0x6b120) at auth-master-connection.c:127 #9 auth_master_input_line (line=<optimized out>, conn=0x6b120) at auth-master-connection.c:598 #10 master_input (conn=0x6b120) at auth-master-connection.c:653 #11 0xff2ecca4 in io_loop_call_io (io=io@entry=0x6b398) at ioloop.c:387 #12 0xff2ed604 in io_loop_handler_run (ioloop=ioloop@entry=0x5e5e8) at ioloop-poll.c:211 #13 0xff2ec7a8 in io_loop_run (ioloop=0x5e5e8) at ioloop.c:406 #14 0xff29ad7c in master_service_run (service=0x5e128, callback=0x27b40 ) at master-service.c:566 #15 0x0001852c in main (argc=1, argv=0xffbffd54) at main.c:393 (gdb) print ctxt $1 = (struct sha1_ctxt *) 0xffbff63c As you can see, ctxt is on a 4-byte boundary, not on an 8-byte boundary. The crash happens at sha1.c:224 where the 8-byte-alignment is indeed mandatory on a SPARC architecture:
ctxt->c.b64[0] += copysiz * 8;
The environment is Solaris 10 on SPARCv9. The sources have been compiled using gcc 4.8.0 for 32 bit.
Andreas.
participants (2)
-
Andreas F. Borchert
-
Timo Sirainen