Fix use-after-free.

Transport and channel have different lifetimes, but share a metadata
context.

Make the metadata context ref counted, and have transport take a ref.
pull/587/head
Craig Tiller 10 years ago
parent efad8fadd3
commit 9be83eec1d
  1. 6
      src/core/security/credentials.c
  2. 2
      src/core/surface/channel.c
  3. 12
      src/core/transport/chttp2_transport.c
  4. 19
      src/core/transport/metadata.c
  5. 3
      src/core/transport/metadata.h
  6. 2
      test/core/channel/channel_stack_test.c
  7. 2
      test/core/channel/metadata_buffer_test.c
  8. 14
      test/core/security/credentials_test.c
  9. 2
      test/core/transport/chttp2/hpack_parser_test.c
  10. 6
      test/core/transport/chttp2/hpack_table_test.c
  11. 2
      test/core/transport/chttp2/stream_encoder_test.c
  12. 18
      test/core/transport/metadata_test.c
  13. 2
      test/core/transport/transport_end2end_tests.c

@ -313,7 +313,7 @@ static void oauth2_token_fetcher_destroy(grpc_credentials *creds) {
grpc_mdelem_unref(c->access_token_md); grpc_mdelem_unref(c->access_token_md);
} }
gpr_mu_destroy(&c->mu); gpr_mu_destroy(&c->mu);
grpc_mdctx_orphan(c->md_ctx); grpc_mdctx_unref(c->md_ctx);
gpr_free(c); gpr_free(c);
} }
@ -587,7 +587,7 @@ static void fake_oauth2_destroy(grpc_credentials *creds) {
if (c->access_token_md != NULL) { if (c->access_token_md != NULL) {
grpc_mdelem_unref(c->access_token_md); grpc_mdelem_unref(c->access_token_md);
} }
grpc_mdctx_orphan(c->md_ctx); grpc_mdctx_unref(c->md_ctx);
gpr_free(c); gpr_free(c);
} }
@ -897,7 +897,7 @@ static void iam_destroy(grpc_credentials *creds) {
grpc_iam_credentials *c = (grpc_iam_credentials *)creds; grpc_iam_credentials *c = (grpc_iam_credentials *)creds;
grpc_mdelem_unref(c->token_md); grpc_mdelem_unref(c->token_md);
grpc_mdelem_unref(c->authority_selector_md); grpc_mdelem_unref(c->authority_selector_md);
grpc_mdctx_orphan(c->md_ctx); grpc_mdctx_unref(c->md_ctx);
gpr_free(c); gpr_free(c);
} }

@ -146,7 +146,7 @@ static void destroy_channel(void *p, int ok) {
grpc_mdstr_unref(channel->grpc_message_string); grpc_mdstr_unref(channel->grpc_message_string);
grpc_mdstr_unref(channel->path_string); grpc_mdstr_unref(channel->path_string);
grpc_mdstr_unref(channel->authority_string); grpc_mdstr_unref(channel->authority_string);
grpc_mdctx_orphan(channel->metadata_context); grpc_mdctx_unref(channel->metadata_context);
gpr_free(channel); gpr_free(channel);
} }

@ -336,11 +336,9 @@ static void recv_data(void *tp, gpr_slice *slices, size_t nslices,
* CONSTRUCTION/DESTRUCTION/REFCOUNTING * CONSTRUCTION/DESTRUCTION/REFCOUNTING
*/ */
static void unref_transport(transport *t) { static void destruct_transport(transport *t) {
size_t i; size_t i;
if (!gpr_unref(&t->refs)) return;
gpr_mu_lock(&t->mu); gpr_mu_lock(&t->mu);
GPR_ASSERT(t->ep == NULL); GPR_ASSERT(t->ep == NULL);
@ -380,9 +378,16 @@ static void unref_transport(transport *t) {
grpc_sopb_destroy(&t->nuke_later_sopb); grpc_sopb_destroy(&t->nuke_later_sopb);
grpc_mdctx_unref(t->metadata_context);
gpr_free(t); gpr_free(t);
} }
static void unref_transport(transport *t) {
if (!gpr_unref(&t->refs)) return;
destruct_transport(t);
}
static void ref_transport(transport *t) { gpr_ref(&t->refs); } static void ref_transport(transport *t) { gpr_ref(&t->refs); }
static void init_transport(transport *t, grpc_transport_setup_callback setup, static void init_transport(transport *t, grpc_transport_setup_callback setup,
@ -401,6 +406,7 @@ static void init_transport(transport *t, grpc_transport_setup_callback setup,
gpr_ref_init(&t->refs, 2); gpr_ref_init(&t->refs, 2);
gpr_mu_init(&t->mu); gpr_mu_init(&t->mu);
gpr_cv_init(&t->cv); gpr_cv_init(&t->cv);
grpc_mdctx_ref(mdctx);
t->metadata_context = mdctx; t->metadata_context = mdctx;
t->str_grpc_timeout = t->str_grpc_timeout =
grpc_mdstr_from_string(t->metadata_context, "grpc-timeout"); grpc_mdstr_from_string(t->metadata_context, "grpc-timeout");

@ -79,7 +79,7 @@ typedef struct internal_metadata {
struct grpc_mdctx { struct grpc_mdctx {
gpr_uint32 hash_seed; gpr_uint32 hash_seed;
int orphaned; int refs;
gpr_mu mu; gpr_mu mu;
@ -114,7 +114,7 @@ static void unlock(grpc_mdctx *ctx) {
mdelems on every unlock (instead of the usual 'I'm too loaded' trigger mdelems on every unlock (instead of the usual 'I'm too loaded' trigger
case), since otherwise we can be stuck waiting for a garbage collection case), since otherwise we can be stuck waiting for a garbage collection
that will never happen. */ that will never happen. */
if (ctx->orphaned) { if (ctx->refs == 0) {
/* uncomment if you're having trouble diagnosing an mdelem leak to make /* uncomment if you're having trouble diagnosing an mdelem leak to make
things clearer (slows down destruction a lot, however) */ things clearer (slows down destruction a lot, however) */
/* gc_mdtab(ctx); */ /* gc_mdtab(ctx); */
@ -139,7 +139,7 @@ static void ref_md(internal_metadata *md) {
grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) { grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed) {
grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx)); grpc_mdctx *ctx = gpr_malloc(sizeof(grpc_mdctx));
ctx->orphaned = 0; ctx->refs = 1;
ctx->hash_seed = seed; ctx->hash_seed = seed;
gpr_mu_init(&ctx->mu); gpr_mu_init(&ctx->mu);
ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY); ctx->strtab = gpr_malloc(sizeof(internal_string *) * INITIAL_STRTAB_CAPACITY);
@ -197,10 +197,17 @@ static void metadata_context_destroy(grpc_mdctx *ctx) {
gpr_free(ctx); gpr_free(ctx);
} }
void grpc_mdctx_orphan(grpc_mdctx *ctx) { void grpc_mdctx_ref(grpc_mdctx *ctx) {
lock(ctx); lock(ctx);
GPR_ASSERT(!ctx->orphaned); GPR_ASSERT(ctx->refs > 0);
ctx->orphaned = 1; ctx->refs++;
unlock(ctx);
}
void grpc_mdctx_unref(grpc_mdctx *ctx) {
lock(ctx);
GPR_ASSERT(ctx->refs > 0);
ctx->refs--;
unlock(ctx); unlock(ctx);
} }

@ -84,7 +84,8 @@ struct grpc_mdelem {
/* Create/orphan a metadata context */ /* Create/orphan a metadata context */
grpc_mdctx *grpc_mdctx_create(void); grpc_mdctx *grpc_mdctx_create(void);
grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed); grpc_mdctx *grpc_mdctx_create_with_seed(gpr_uint32 seed);
void grpc_mdctx_orphan(grpc_mdctx *mdctx); void grpc_mdctx_ref(grpc_mdctx *mdctx);
void grpc_mdctx_unref(grpc_mdctx *mdctx);
/* Test only accessors to internal state - only for testing this code - do not /* Test only accessors to internal state - only for testing this code - do not
rely on it outside of metadata_test.c */ rely on it outside of metadata_test.c */

@ -128,7 +128,7 @@ static void test_create_channel_stack(void) {
grpc_channel_stack_destroy(channel_stack); grpc_channel_stack_destroy(channel_stack);
gpr_free(channel_stack); gpr_free(channel_stack);
grpc_mdctx_orphan(metadata_context); grpc_mdctx_unref(metadata_context);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

@ -182,7 +182,7 @@ static void test_case(size_t key_prefix_len, size_t value_prefix_len,
gpr_free(stk); gpr_free(stk);
grpc_metadata_buffer_destroy(&buffer, GRPC_OP_OK); grpc_metadata_buffer_destroy(&buffer, GRPC_OP_OK);
grpc_mdctx_orphan(mdctx); grpc_mdctx_unref(mdctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

@ -138,7 +138,7 @@ static void test_oauth2_token_fetcher_creds_parsing_ok(void) {
GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->value), GPR_ASSERT(!strcmp(grpc_mdstr_as_c_string(token_elem->value),
"Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_")); "Bearer ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_"));
grpc_mdelem_unref(token_elem); grpc_mdelem_unref(token_elem);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) { static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) {
@ -150,7 +150,7 @@ static void test_oauth2_token_fetcher_creds_parsing_bad_http_status(void) {
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) { static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) {
@ -161,7 +161,7 @@ static void test_oauth2_token_fetcher_creds_parsing_empty_http_body(void) {
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) { static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) {
@ -176,7 +176,7 @@ static void test_oauth2_token_fetcher_creds_parsing_invalid_json(void) {
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) { static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) {
@ -190,7 +190,7 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token(void) {
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) { static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) {
@ -205,7 +205,7 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token_type(void) {
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime( static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime(
@ -220,7 +220,7 @@ static void test_oauth2_token_fetcher_creds_parsing_missing_token_lifetime(
GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response( GPR_ASSERT(grpc_oauth2_token_fetcher_credentials_parse_server_response(
&response, ctx, &token_elem, &token_lifetime) == &response, ctx, &token_elem, &token_lifetime) ==
GRPC_CREDENTIALS_ERROR); GRPC_CREDENTIALS_ERROR);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void check_metadata(expected_md *expected, grpc_mdelem **md_elems, static void check_metadata(expected_md *expected, grpc_mdelem **md_elems,

@ -214,7 +214,7 @@ static void test_vectors(grpc_slice_split_mode mode) {
"set-cookie", "set-cookie",
"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL); "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
grpc_chttp2_hpack_parser_destroy(&parser); grpc_chttp2_hpack_parser_destroy(&parser);
grpc_mdctx_orphan(mdctx); grpc_mdctx_unref(mdctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

@ -126,7 +126,7 @@ static void test_static_lookup(void) {
assert_index(&tbl, 61, "www-authenticate", ""); assert_index(&tbl, 61, "www-authenticate", "");
grpc_chttp2_hptbl_destroy(&tbl); grpc_chttp2_hptbl_destroy(&tbl);
grpc_mdctx_orphan(mdctx); grpc_mdctx_unref(mdctx);
} }
static void test_many_additions(void) { static void test_many_additions(void) {
@ -158,7 +158,7 @@ static void test_many_additions(void) {
} }
grpc_chttp2_hptbl_destroy(&tbl); grpc_chttp2_hptbl_destroy(&tbl);
grpc_mdctx_orphan(mdctx); grpc_mdctx_unref(mdctx);
} }
static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl, static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
@ -262,7 +262,7 @@ static void test_find(void) {
GPR_ASSERT(r.has_value == 0); GPR_ASSERT(r.has_value == 0);
grpc_chttp2_hptbl_destroy(&tbl); grpc_chttp2_hptbl_destroy(&tbl);
grpc_mdctx_orphan(mdctx); grpc_mdctx_unref(mdctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

@ -309,7 +309,7 @@ static void run_test(void (*test)(), const char *name) {
grpc_sopb_init(&g_sopb); grpc_sopb_init(&g_sopb);
test(); test();
grpc_chttp2_hpack_compressor_destroy(&g_compressor); grpc_chttp2_hpack_compressor_destroy(&g_compressor);
grpc_mdctx_orphan(g_mdctx); grpc_mdctx_unref(g_mdctx);
grpc_sopb_destroy(&g_sopb); grpc_sopb_destroy(&g_sopb);
} }

@ -52,7 +52,7 @@ static void test_no_op(void) {
LOG_TEST(); LOG_TEST();
ctx = grpc_mdctx_create(); ctx = grpc_mdctx_create();
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_create_string(void) { static void test_create_string(void) {
@ -71,7 +71,7 @@ static void test_create_string(void) {
GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0); GPR_ASSERT(gpr_slice_str_cmp(s3->slice, "very much not hello") == 0);
grpc_mdstr_unref(s1); grpc_mdstr_unref(s1);
grpc_mdstr_unref(s2); grpc_mdstr_unref(s2);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
grpc_mdstr_unref(s3); grpc_mdstr_unref(s3);
} }
@ -95,7 +95,7 @@ static void test_create_metadata(void) {
grpc_mdelem_unref(m1); grpc_mdelem_unref(m1);
grpc_mdelem_unref(m2); grpc_mdelem_unref(m2);
grpc_mdelem_unref(m3); grpc_mdelem_unref(m3);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_create_many_ephemeral_metadata(void) { static void test_create_many_ephemeral_metadata(void) {
@ -116,7 +116,7 @@ static void test_create_many_ephemeral_metadata(void) {
/* capacity should not grow */ /* capacity should not grow */
GPR_ASSERT(mdtab_capacity_before == GPR_ASSERT(mdtab_capacity_before ==
grpc_mdctx_get_mdtab_capacity_test_only(ctx)); grpc_mdctx_get_mdtab_capacity_test_only(ctx));
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_create_many_persistant_metadata(void) { static void test_create_many_persistant_metadata(void) {
@ -145,7 +145,7 @@ static void test_create_many_persistant_metadata(void) {
for (i = 0; i < MANY; i++) { for (i = 0; i < MANY; i++) {
grpc_mdelem_unref(created[i]); grpc_mdelem_unref(created[i]);
} }
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
gpr_free(created); gpr_free(created);
} }
@ -171,7 +171,7 @@ static void test_spin_creating_the_same_thing(void) {
GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1); GPR_ASSERT(grpc_mdctx_get_mdtab_count_test_only(ctx) == 1);
GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1); GPR_ASSERT(grpc_mdctx_get_mdtab_free_test_only(ctx) == 1);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_things_stick_around(void) { static void test_things_stick_around(void) {
@ -218,7 +218,7 @@ static void test_things_stick_around(void) {
} }
} }
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
gpr_free(strs); gpr_free(strs);
gpr_free(shuf); gpr_free(shuf);
} }
@ -245,7 +245,7 @@ static void test_slices_work(void) {
gpr_slice_unref(slice); gpr_slice_unref(slice);
grpc_mdstr_unref(str); grpc_mdstr_unref(str);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
static void test_base64_and_huffman_works(void) { static void test_base64_and_huffman_works(void) {
@ -264,7 +264,7 @@ static void test_base64_and_huffman_works(void) {
gpr_slice_unref(slice2); gpr_slice_unref(slice2);
grpc_mdstr_unref(str); grpc_mdstr_unref(str);
grpc_mdctx_orphan(ctx); grpc_mdctx_unref(ctx);
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {

@ -927,7 +927,7 @@ void grpc_transport_end2end_tests(grpc_transport_test_config *config) {
test_request_with_flow_ctl_cb(config, interesting_message_lengths[i]); test_request_with_flow_ctl_cb(config, interesting_message_lengths[i]);
} }
grpc_mdctx_orphan(g_metadata_context); grpc_mdctx_unref(g_metadata_context);
gpr_log(GPR_INFO, "tests completed ok"); gpr_log(GPR_INFO, "tests completed ok");
} }

Loading…
Cancel
Save