summaryrefslogtreecommitdiff
path: root/erts/emulator
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2019-11-06 13:03:36 +0100
committerRickard Green <rickard@erlang.org>2019-11-06 13:03:36 +0100
commit42de74576b254a1a24fd050018150fd823451ba6 (patch)
tree45bb370607db9b27acf38732cbd9e8541202e5c7 /erts/emulator
parentb10d87f13335077a7593724db59a1ed3ef4e4122 (diff)
parent843b70004bbd5845137fc35b3fe1da98edef1e8a (diff)
downloaderlang-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
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/beam/erl_bif_unique.h4
-rw-r--r--erts/emulator/beam/erl_node_tables.c11
-rw-r--r--erts/emulator/beam/sys.h26
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)
{