From b77b5c191513f5f281e72a51e6b2de29e2d2d7a6 Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Tue, 28 May 2024 14:21:35 -0700 Subject: merge revision(s) 5e0c17145131e073814c7e5b15227d0b4e73cabe: [Backport #20169] Make io_fwrite safe for compaction [Bug #20169] Embedded strings are not safe for system calls without the GVL because compaction can cause pages to be locked causing the operation to fail with EFAULT. This commit changes io_fwrite to use rb_str_tmp_frozen_no_embed_acquire, which guarantees that the return string is not embedded. --- string.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'string.c') diff --git a/string.c b/string.c index 04162ef4d0..702024671a 100644 --- a/string.c +++ b/string.c @@ -1341,6 +1341,42 @@ rb_str_tmp_frozen_acquire(VALUE orig) return str_new_frozen_buffer(0, orig, FALSE); } +VALUE +rb_str_tmp_frozen_no_embed_acquire(VALUE orig) +{ + if (OBJ_FROZEN_RAW(orig) && !STR_EMBED_P(orig) && !rb_str_reembeddable_p(orig)) return orig; + if (STR_SHARED_P(orig) && !STR_EMBED_P(RSTRING(orig)->as.heap.aux.shared)) return rb_str_tmp_frozen_acquire(orig); + + VALUE str = str_alloc_heap(0); + OBJ_FREEZE(str); + /* Always set the STR_SHARED_ROOT to ensure it does not get re-embedded. */ + FL_SET(str, STR_SHARED_ROOT); + + size_t capa = str_capacity(orig, TERM_LEN(orig)); + + /* If the string is embedded then we want to create a copy that is heap + * allocated. If the string is shared then the shared root must be + * embedded, so we want to create a copy. If the string is a shared root + * then it must be embedded, so we want to create a copy. */ + if (STR_EMBED_P(orig) || FL_TEST_RAW(orig, STR_SHARED | STR_SHARED_ROOT)) { + RSTRING(str)->as.heap.ptr = rb_xmalloc_mul_add_mul(sizeof(char), capa, sizeof(char), TERM_LEN(orig)); + memcpy(RSTRING(str)->as.heap.ptr, RSTRING_PTR(orig), capa); + } + else { + /* orig must be heap allocated and not shared, so we can safely transfer + * the pointer to str. */ + RSTRING(str)->as.heap.ptr = RSTRING(orig)->as.heap.ptr; + RBASIC(str)->flags |= RBASIC(orig)->flags & STR_NOFREE; + RBASIC(orig)->flags &= ~STR_NOFREE; + STR_SET_SHARED(orig, str); + } + + RSTRING(str)->len = RSTRING(orig)->len; + RSTRING(str)->as.heap.aux.capa = capa; + + return str; +} + void rb_str_tmp_frozen_release(VALUE orig, VALUE tmp) { -- cgit v1.2.3