merge revision(s) 2a76440fac62b: [Backport #18501]

[Bug #18501] Fire write barrier after hash has been written

	Before this change the write barrier was executed before the key and
	value were actually reachable via the Hash.  This could cause
	inconsistencies in object coloration which would lead to accidental
	collection of dup'd keys.

	Example:

	1. Object O is grey, Object P is white.
	2. Write barrier fires O -> P
	3. Write barrier does nothing
	4. Malloc happens, which starts GC
	5. GC colors O black
	6. P is written in to O (now we have O -> P reference)
	7. P is now accidentally treated as garbage
	---
	 hash.c | 20 +++++++++++++++-----
	 1 file changed, 15 insertions(+), 5 deletions(-)
This commit is contained in:
NARUSE, Yui 2022-02-07 19:58:46 +09:00
parent e54289bb63
commit 86c8e15170
2 changed files with 16 additions and 6 deletions

20
hash.c
View File

@ -1673,6 +1673,8 @@ struct update_arg {
st_data_t arg;
st_update_callback_func *func;
VALUE hash;
VALUE key;
VALUE value;
};
typedef int (*tbl_update_func)(st_data_t *, st_data_t *, st_data_t, int);
@ -1705,11 +1707,11 @@ tbl_update_modify(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
default:
break;
case ST_CONTINUE:
if (!existing || *key != old_key || *val != old_value)
if (!existing || *key != old_key || *val != old_value) {
rb_hash_modify(hash);
/* write barrier */
RB_OBJ_WRITTEN(hash, Qundef, *key);
RB_OBJ_WRITTEN(hash, Qundef, *val);
p->key = *key;
p->value = *val;
}
break;
case ST_DELETE:
if (existing)
@ -1727,9 +1729,17 @@ tbl_update(VALUE hash, VALUE key, tbl_update_func func, st_data_t optional_arg)
.arg = optional_arg,
.func = func,
.hash = hash,
.key = key,
.value = (VALUE)optional_arg,
};
return rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
int ret = rb_hash_stlike_update(hash, key, tbl_update_modify, (st_data_t)&arg);
/* write barrier */
RB_OBJ_WRITTEN(hash, Qundef, arg.key);
RB_OBJ_WRITTEN(hash, Qundef, arg.value);
return ret;
}
#define UPDATE_CALLBACK(iter_lev, func) ((iter_lev) > 0 ? func##_noinsert : func##_insert)

View File

@ -11,7 +11,7 @@
# define RUBY_VERSION_MINOR RUBY_API_VERSION_MINOR
#define RUBY_VERSION_TEENY 0
#define RUBY_RELEASE_DATE RUBY_RELEASE_YEAR_STR"-"RUBY_RELEASE_MONTH_STR"-"RUBY_RELEASE_DAY_STR
#define RUBY_PATCHLEVEL 14
#define RUBY_PATCHLEVEL 15
#define RUBY_RELEASE_YEAR 2022
#define RUBY_RELEASE_MONTH 2