This bug is due to a double-free in source3/librpc/crypto/gse.c where gse_ctx->k5ctx is freed twice if gse_context_init fails and the err_out path is taken.
The beginning of gse_context_init calls talloc_set_destructor to call gse_context_destructor:
This gse_context_destructor callback function is triggered by calls to TALLOC_FREE(gse_ctx) and frees pointers stored in the gse_ctx structure including gse_ctx->k5ctx:
if (gse_ctx->k5ctx) {
if (gse_ctx->ccache) { krb5_cc_close(gse_ctx->k5ctx, gse_ctx->ccache); gse_ctx->ccache = NULL;
}
if (gse_ctx->keytab) { krb5_kt_close(gse_ctx->k5ctx, gse_ctx->keytab); gse_ctx->keytab = NULL;
} krb5_free_context(gse_ctx->k5ctx); gse_ctx->k5ctx = NULL;
}
However, if gse_context_init fails and takes the err_out path, gse_ctx->k5ctx is freed without setting that pointer to NULL and then immediately calls TALLOC_FREE(gse_ctx) which then attempts to free gse_ctx->k5ctx a second time:
err_out:
if (gse_ctx->k5ctx) { krb5_free_context(gse_ctx->k5ctx);
}
TALLOC_FREE(gse_ctx);
This results in the following double-free stack trace:
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7443859 in __GI_abort () at abort.c:79
#2 0x00007ffff74ae3ee in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff75d8285 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#3 0x00007ffff74b647c in malloc_printerr (str=str@entry=0x7ffff75da5d0 "free(): double free detected in tcache 2") at malloc.c:5347
#4 0x00007ffff74b80ed in _int_free (av=0x7ffff7609b80 <main_arena>, p=0x5555555ce2c0, have_lock=0) at malloc.c:4201
#5 0x00007ffff6cc29f9 in krb5_free_context (context=0x5555555cdcd0) at ../../source4/heimdal/lib/krb5/context.c:595
#6 0x00007ffff73e75d1 in gse_context_destructor (ptr=ptr@entry=0x5555555cdc50) at ../../source3/librpc/crypto/gse.c:84
#7 0x00007ffff776553e in _tc_free_internal (tc=0x5555555cdbf0, location=0x7ffff73f2480 "../../source3/librpc/crypto/gse.c:241") at ../../talloc.c:1157
#8 0x00007ffff73e826c in gse_context_init (mem_ctx=mem_ctx@entry=0x5555555cdb60, do_sign=<optimized out>, do_seal=<optimized out>, add_gss_c_flags=<optimized out>, _gse_ctx=_gse_ctx@entry=0x7fffffffd500,
ccache_name=<optimized out>) at ../../source3/librpc/crypto/gse.c:241
#9 0x00007ffff73e8433 in gse_init_client (ccache_name=0x0, realm=<optimized out>, username=<optimized out>, password=<optimized out>, _gse_ctx=<synthetic pointer>, add_gss_c_flags=<optimized out>,
service=0x5555555ccdf0 "cifs", server=0x5555555ccfb0 "freenas", do_seal=<optimized out>, do_sign=<optimized out>, mem_ctx=0x5555555cdb60) at ../../source3/librpc/crypto/gse.c:268
#10 gensec_gse_client_start (gensec_security=0x5555555cdb60) at ../../source3/librpc/crypto/gse.c:786
#11 0x00007ffff7390453 in gensec_start_mech (gensec_security=0x5555555cdb60) at ../../auth/gensec/gensec_start.c:743
#12 gensec_start_mech (gensec_security=0x5555555cdb60) at ../../auth/gensec/gensec_start.c:704
#13 0x00007ffff7387822 in gensec_spnego_client_negTokenInit_step (gensec_security=0x5555555c9b70, spnego_state=0x5555555cc1b0, n=0x5555555cd4a0, spnego_in=<optimized out>, last_status=...,
in_mem_ctx=<optimized out>, in_next=0x5555555cd3f8) at ../../auth/gensec/spnego.c:633
#14 0x00007ffff7387e02 in gensec_spnego_client_negTokenInit_start (gensec_security=0x5555555c9b70, spnego_state=0x5555555cc1b0, n=0x5555555cd4a0, spnego_in=0x5555555cd368, in_mem_ctx=0x5555555cd340,
in_next=0x5555555cd3f8) at ../../auth/gensec/spnego.c:537
#15 0x00007ffff7388b84 in gensec_spnego_update_pre (req=0x5555555cd190) at ../../auth/gensec/spnego.c:1943
#16 gensec_spnego_update_send (mem_ctx=<optimized out>, ev=0x5555555aee90, gensec_security=<optimized out>, in=...) at ../../auth/gensec/spnego.c:1741
#17 0x00007ffff738f3b0 in gensec_update_send (mem_ctx=<optimized out>, ev=0x5555555aee90, gensec_security=0x5555555c9b70, in=...) at ../../auth/gensec/gensec.c:449
#18 0x00007ffff7f6dba6 in cli_session_setup_gensec_local_next (req=0x5555555c9d90) at ../../source3/libsmb/cliconnect.c:997
#19 0x00007ffff7f6f520 in cli_session_setup_gensec_send (target_service=0x7ffff7fa478e "cifs", target_hostname=0x5555555ca480 "freenas", creds=0x5555555adf90, cli=0x5555555adf90, ev=0x5555555aee90,
mem_ctx=<optimized out>) at ../../source3/libsmb/cliconnect.c:977
#20 cli_session_setup_spnego_send (creds=0x5555555adf90, cli=0x5555555adf90, ev=0x5555555aee90, mem_ctx=<optimized out>) at ../../source3/libsmb/cliconnect.c:1346
#21 cli_session_setup_creds_send (mem_ctx=mem_ctx@entry=0x5555555aee90, ev=ev@entry=0x5555555aee90, cli=cli@entry=0x5555555adf90, creds=creds@entry=0x5555555b4380) at ../../source3/libsmb/cliconnect.c:1505
#22 0x00007ffff7f6fcad in cli_session_setup_creds (cli=0x5555555adf90, creds=creds@entry=0x5555555b4380) at ../../source3/libsmb/cliconnect.c:1843
#23 0x00007ffff7f8ca57 in do_connect (ctx=ctx@entry=0x5555555aae90, server=<optimized out>, server@entry=0x5555555b4790 "freenas", share=share@entry=0x555555571183 "IPC$", auth_info=0x5555555b42f0,
force_encrypt=<optimized out>, max_protocol=max_protocol@entry=13, port=0, name_type=32, pcli=0x7fffffffd8a0) at ../../source3/libsmb/clidfs.c:236
#24 0x00007ffff7f8cfa8 in cli_cm_connect (ctx=ctx@entry=0x5555555aae90, referring_cli=referring_cli@entry=0x0, server=server@entry=0x5555555b4790 "freenas", share=share@entry=0x555555571183 "IPC$",
auth_info=<optimized out>, force_encrypt=force_encrypt@entry=false, max_protocol=13, port=0, name_type=32, pcli=0x7fffffffd900) at ../../source3/libsmb/clidfs.c:339
#25 0x00007ffff7f8d13f in cli_cm_open (ctx=0x5555555aae90, referring_cli=0x0, server=0x5555555b4790 "freenas", share=0x555555571183 "IPC$", auth_info=<optimized out>, force_encrypt=<optimized out>,
max_protocol=13, port=0, name_type=32, pcli=0x55555557c398 <cli>) at ../../source3/libsmb/clidfs.c:441
#26 0x000055555555edc0 in do_host_query (query_host=0x5555555b4790 "freenas") at ../../source3/client/client.c:6702
#27 main (argc=<optimized out>, argv=<optimized out>) at ../../source3/client/client.c:6702
(gdb)
When explicitly setting gse_ctx->k5ctx to NULL after the free, this crash no longer occurs and I can now access SMB shares successfully:
err_out:
if (gse_ctx->k5ctx) { krb5_free_context(gse_ctx->k5ctx); gse_ctx->k5ctx = NULL;
}
TALLOC_FREE(gse_ctx);
This bug appears to have been fixed upstream by removing the explicit krb5_free_context call here and depending on gse_context_destructor to free the k5ctx member pointer:
This bug is due to a double-free in source3/ librpc/ crypto/ gse.c where gse_ctx->k5ctx is freed twice if gse_context_init fails and the err_out path is taken.
The beginning of gse_context_init calls talloc_ set_destructor to call gse_context_ destructor:
This gse_context_ destructor callback function is triggered by calls to TALLOC_ FREE(gse_ ctx) and frees pointers stored in the gse_ctx structure including gse_ctx->k5ctx:
if (gse_ctx->k5ctx) {
krb5_ cc_close( gse_ctx- >k5ctx, gse_ctx->ccache);
gse_ ctx->ccache = NULL;
krb5_ kt_close( gse_ctx- >k5ctx, gse_ctx->keytab);
gse_ ctx->keytab = NULL;
krb5_ free_context( gse_ctx- >k5ctx) ;
gse_ctx- >k5ctx = NULL;
if (gse_ctx->ccache) {
}
if (gse_ctx->keytab) {
}
}
However, if gse_context_init fails and takes the err_out path, gse_ctx->k5ctx is freed without setting that pointer to NULL and then immediately calls TALLOC_ FREE(gse_ ctx) which then attempts to free gse_ctx->k5ctx a second time:
err_out:
krb5_ free_context( gse_ctx- >k5ctx) ;
if (gse_ctx->k5ctx) {
}
This results in the following double-free stack trace:
(gdb) bt unix/sysv/ linux/raise. c:50 action@ entry=do_ abort, fmt=fmt@ entry=0x7ffff75 d8285 "%s\n") at ../sysdeps/ posix/libc_ fatal.c: 155 entry=0x7ffff75 da5d0 "free(): double free detected in tcache 2") at malloc.c:5347 0x5555555cdcd0) at ../../source4/ heimdal/ lib/krb5/ context. c:595 destructor (ptr=ptr@ entry=0x5555555 cdc50) at ../../source3/ librpc/ crypto/ gse.c:84 0x7ffff73f2480 "../../ source3/ librpc/ crypto/ gse.c:241" ) at ../../talloc.c:1157 mem_ctx@ entry=0x5555555 cdb60, do_sign=<optimized out>, do_seal=<optimized out>, add_gss_ c_flags= <optimized out>, _gse_ctx= _gse_ctx@ entry=0x7ffffff fd500, name=<optimized out>) at ../../source3/ librpc/ crypto/ gse.c:241 c_flags= <optimized out>, 0x5555555ccdf0 "cifs", server= 0x5555555ccfb0 "freenas", do_seal=<optimized out>, do_sign=<optimized out>, mem_ctx= 0x5555555cdb60) at ../../source3/ librpc/ crypto/ gse.c:268 gse_client_ start (gensec_ security= 0x5555555cdb60) at ../../source3/ librpc/ crypto/ gse.c:786 security= 0x5555555cdb60) at ../../auth/ gensec/ gensec_ start.c: 743 security= 0x5555555cdb60) at ../../auth/ gensec/ gensec_ start.c: 704 spnego_ client_ negTokenInit_ step (gensec_ security= 0x5555555c9b70, spnego_ state=0x5555555 cc1b0, n=0x5555555cd4a0, spnego_ in=<optimized out>, last_status=..., mem_ctx= <optimized out>, in_next= 0x5555555cd3f8) at ../../auth/ gensec/ spnego. c:633 spnego_ client_ negTokenInit_ start (gensec_ security= 0x5555555c9b70, spnego_ state=0x5555555 cc1b0, n=0x5555555cd4a0, spnego_ in=0x5555555cd3 68, in_mem_ ctx=0x5555555cd 340, next=0x5555555c d3f8) at ../../auth/ gensec/ spnego. c:537 spnego_ update_ pre (req=0x5555555c d190) at ../../auth/ gensec/ spnego. c:1943 spnego_ update_ send (mem_ctx=<optimized out>, ev=0x5555555aee90, gensec_ security= <optimized out>, in=...) at ../../auth/ gensec/ spnego. c:1741 security= 0x5555555c9b70, in=...) at ../../auth/ gensec/ gensec. c:449 setup_gensec_ local_next (req=0x5555555c 9d90) at ../../source3/ libsmb/ cliconnect. c:997 setup_gensec_ send (target_ service= 0x7ffff7fa478e "cifs", target_ hostname= 0x5555555ca480 "freenas", creds=0x5555555 adf90, cli=0x5555555adf90, ev=0x5555555aee90, ctx=<optimized out>) at ../../source3/ libsmb/ cliconnect. c:977 setup_spnego_ send (creds= 0x5555555adf90, cli=0x5555555adf90, ev=0x5555555aee90, mem_ctx=<optimized out>) at ../../source3/ libsmb/ cliconnect. c:1346 setup_creds_ send (mem_ctx= mem_ctx@ entry=0x5555555 aee90, ev=ev@entry= 0x5555555aee90, cli=cli@ entry=0x5555555 adf90, creds=creds@ entry=0x5555555 b4380) at ../../source3/ libsmb/ cliconnect. c:1505 setup_creds (cli=0x5555555a df90, creds=creds@ entry=0x5555555 b4380) at ../../source3/ libsmb/ cliconnect. c:1843 entry=0x5555555 aae90, server=<optimized out>, server@ entry=0x5555555 b4790 "freenas", share=share@ entry=0x5555555 71183 "IPC$", auth_info= 0x5555555b42f0, encrypt= <optimized out>, max_protocol= max_protocol@ entry=13, port=0, name_type=32, pcli=0x7fffffff d8a0) at ../../source3/ libsmb/ clidfs. c:236 entry=0x5555555 aae90, referring_ cli=referring_ cli@entry= 0x0, server= server@ entry=0x5555555 b4790 "freenas", share=share@ entry=0x5555555 71183 "IPC$", info=<optimized out>, force_encrypt= force_encrypt@ entry=false, max_protocol=13, port=0, name_type=32, pcli=0x7fffffff d900) at ../../source3/ libsmb/ clidfs. c:339 ae90, referring_cli=0x0, server= 0x5555555b4790 "freenas", share=0x5555555 71183 "IPC$", auth_info= <optimized out>, force_encrypt= <optimized out>, protocol= 13, port=0, name_type=32, pcli=0x55555557c398 <cli>) at ../../source3/ libsmb/ clidfs. c:441 host=0x5555555b 4790 "freenas") at ../../source3/ client/ client. c:6702 client/ client. c:6702
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/
#1 0x00007ffff7443859 in __GI_abort () at abort.c:79
#2 0x00007ffff74ae3ee in __libc_message (action=
#3 0x00007ffff74b647c in malloc_printerr (str=str@
#4 0x00007ffff74b80ed in _int_free (av=0x7ffff7609b80 <main_arena>, p=0x5555555ce2c0, have_lock=0) at malloc.c:4201
#5 0x00007ffff6cc29f9 in krb5_free_context (context=
#6 0x00007ffff73e75d1 in gse_context_
#7 0x00007ffff776553e in _tc_free_internal (tc=0x5555555cdbf0, location=
#8 0x00007ffff73e826c in gse_context_init (mem_ctx=
ccache_
#9 0x00007ffff73e8433 in gse_init_client (ccache_name=0x0, realm=<optimized out>, username=<optimized out>, password=<optimized out>, _gse_ctx=<synthetic pointer>, add_gss_
service=
#10 gensec_
#11 0x00007ffff7390453 in gensec_start_mech (gensec_
#12 gensec_start_mech (gensec_
#13 0x00007ffff7387822 in gensec_
in_
#14 0x00007ffff7387e02 in gensec_
in_
#15 0x00007ffff7388b84 in gensec_
#16 gensec_
#17 0x00007ffff738f3b0 in gensec_update_send (mem_ctx=<optimized out>, ev=0x5555555aee90, gensec_
#18 0x00007ffff7f6dba6 in cli_session_
#19 0x00007ffff7f6f520 in cli_session_
mem_
#20 cli_session_
#21 cli_session_
#22 0x00007ffff7f6fcad in cli_session_
#23 0x00007ffff7f8ca57 in do_connect (ctx=ctx@
force_
#24 0x00007ffff7f8cfa8 in cli_cm_connect (ctx=ctx@
auth_
#25 0x00007ffff7f8d13f in cli_cm_open (ctx=0x5555555a
max_
#26 0x000055555555edc0 in do_host_query (query_
#27 main (argc=<optimized out>, argv=<optimized out>) at ../../source3/
(gdb)
When explicitly setting gse_ctx->k5ctx to NULL after the free, this crash no longer occurs and I can now access SMB shares successfully:
err_out:
krb5_ free_context( gse_ctx- >k5ctx) ;
gse_ctx- >k5ctx = NULL;
if (gse_ctx->k5ctx) {
}
This bug appears to have been fixed upstream by removing the explicit krb5_free_context call here and depending on gse_context_ destructor to free the k5ctx member pointer:
err_out:
TALLOC_ FREE(gse_ ctx);