diff options
author | Rickard Green <rickard@erlang.org> | 2019-11-06 13:03:36 +0100 |
---|---|---|
committer | Rickard Green <rickard@erlang.org> | 2019-11-06 13:03:36 +0100 |
commit | 42de74576b254a1a24fd050018150fd823451ba6 (patch) | |
tree | 45bb370607db9b27acf38732cbd9e8541202e5c7 | |
parent | b10d87f13335077a7593724db59a1ed3ef4e4122 (diff) | |
parent | 843b70004bbd5845137fc35b3fe1da98edef1e8a (diff) | |
download | erlang-42de74576b254a1a24fd050018150fd823451ba6.tar.gz |
Merge branch 'sverker/dist-entry-refc-bug/ERL-1044/OTP-16224' into maint
* sverker/dist-entry-refc-bug/ERL-1044/OTP-16224:
erts: Fix bug causing leak of DistEntry
erts: Fix write-after-free bug for DistEntry
-rw-r--r-- | erts/emulator/beam/erl_bif_unique.h | 4 | ||||
-rw-r--r-- | erts/emulator/beam/erl_node_tables.c | 11 | ||||
-rw-r--r-- | erts/emulator/beam/sys.h | 26 |
3 files changed, 32 insertions, 9 deletions
diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h index 944788c67c..f5f07da431 100644 --- a/erts/emulator/beam/erl_bif_unique.h +++ b/erts/emulator/beam/erl_bif_unique.h @@ -297,7 +297,7 @@ erts_iref_storage_save(ErtsIRefStorage *iref, Eterm ref) ASSERT(is_magic_ref_thing(hp)); iref->is_magic = 1; iref->u.mb = mrtp->mb; - erts_refc_inc(&mrtp->mb->intern.refc, 1); + erts_refc_inc(&mrtp->mb->intern.refc, 2); } } @@ -337,7 +337,7 @@ erts_iref_storage_make_ref(ErtsIRefStorage *iref, * refc increment of the cleaned storage... */ if (!clean_storage) - erts_refc_inc(&iref->u.mb->intern.refc, 1); + erts_refc_inc(&iref->u.mb->intern.refc, 2); } #ifdef DEBUG diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index 8863e219e2..c2a7679acd 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -287,18 +287,16 @@ static ERTS_INLINE DistEntry *find_dist_entry(Eterm sysname, if (connected_only && is_nil(res->cid)) res = NULL; else { - int pend_delete; erts_aint_t refc; if (inc_refc) { refc = de_refc_inc_read(res, 1); - pend_delete = refc < 2; + if (refc < 2) /* Pending delete */ + de_refc_inc(res, 1); } else { - refc = de_refc_read(res, 0); - pend_delete = refc < 1; + /* Inc from 0 to 1 for pending delete */ + erts_refc_inc_if(&ErtsDistEntry2Bin(res)->intern.refc, 0, 0); } - if (pend_delete) /* Pending delete */ - de_refc_inc(res, 1); } } erts_rwmtx_runlock(&erts_dist_table_rwmtx); @@ -398,6 +396,7 @@ erts_build_dhandle(Eterm **hpp, ErlOffHeap* ohp, Eterm mref, dhandle; ASSERT(bin); ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_dist_entry_destructor); + erts_refc_inc_if(&bin->intern.refc, 0, 0); /* inc for pending delete */ mref = erts_mk_magic_ref(hpp, ohp, bin); dhandle = TUPLE2(*hpp, make_small(conn_id), mref); *hpp += 3; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index c261c8e117..c534891f30 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -901,6 +901,9 @@ ERTS_GLB_INLINE void erts_refc_inc(erts_refc_t *refcp, erts_aint_t min_val); ERTS_GLB_INLINE erts_aint_t erts_refc_inc_unless(erts_refc_t *refcp, erts_aint_t unless_val, erts_aint_t min_val); +ERTS_GLB_INLINE void erts_refc_inc_if(erts_refc_t *refcp, + erts_aint_t if_val, + erts_aint_t min_val); ERTS_GLB_INLINE erts_aint_t erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val); ERTS_GLB_INLINE void erts_refc_dec(erts_refc_t *refcp, erts_aint_t min_val); @@ -942,7 +945,7 @@ erts_refc_inc_unless(erts_refc_t *refcp, while (1) { erts_aint_t exp, new; #ifdef ERTS_REFC_DEBUG - if (val < 0) + if (val < min_val) erts_exit(ERTS_ABORT_EXIT, "erts_refc_inc_unless(): Bad refc found (refc=%ld < %ld)!\n", val, min_val); @@ -957,6 +960,27 @@ erts_refc_inc_unless(erts_refc_t *refcp, } } +ERTS_GLB_INLINE void +erts_refc_inc_if(erts_refc_t *refcp, + erts_aint_t if_val, + erts_aint_t min_val) +{ + erts_aint_t val = erts_atomic_read_nob((erts_atomic_t *) refcp); +#ifdef ERTS_REFC_DEBUG + if (val < min_val) + erts_exit(ERTS_ABORT_EXIT, + "erts_refc_inc_unless(): Bad refc found (refc=%ld < %ld)!\n", + val, min_val); +#endif + if (val == if_val) { + erts_atomic_cmpxchg_nob((erts_atomic_t *) refcp, val+1, val); + /* + * Ignore failure, as it means someone else took care of 'if_val'. + * Could be this function racing with itself. + */ + } +} + ERTS_GLB_INLINE erts_aint_t erts_refc_inctest(erts_refc_t *refcp, erts_aint_t min_val) { |