dep-protobuf/ruby/tests
Jeremy Daer 7d7d6ab836 Ruby: fix use-after-free of map keys aliasing a temporary String (#29026)
Fixes #29023.

`Map#[]=` and `Message.new(map_field: {...})` build the map key as a `upb_StringView`
aliasing a Ruby String, then convert the value before `upb_Map_Set` copies the key. The
value conversion allocates, so it can trigger GC inside that window.

The aliased String is frequently a **temporary**: `Convert_RubyToUpb` replaces the caller's
object when the key is a Symbol (via `to_s`) or a String not already tagged UTF-8 (via
`Convert_CheckStringUtf8`), and nothing references the result once it returns. When GC
collects it, the freed block goes straight back to the next `upb_Arena_Malloc`, which
memcpys the *value* into it — leaving a silently corrupted key holding unrelated heap bytes,
tagged UTF-8 while containing invalid UTF-8, which then propagates into `encode`/`to_json`.

## The fix

Pass the arena at both insertion sites, so the key is copied before anything can allocate.

The lookup paths (`Map_index`, `Map_has_key`, `Map_delete`) keep the `NULL` fast path — they
consume the key immediately with no allocation in between, which is exactly the precondition
`Convert_StringData`'s comment describes. I reworded that comment, since it read as though
the aliasing were unconditionally safe; it holds for three of its five callers and not for
the two that insert.

Cost is one arena allocation per insert for string-typed keys. Non-string keys don't reach
`Convert_StringData` at all.

## Trigger

Needs both:

- a key that is a **Symbol**, or a String not already tagged UTF-8 — `ASCII-8BIT` is the
  common case for anything read from a socket, a file, `Marshal`, or `String#pack`; and
- a value whose conversion allocates (a Symbol, or a non-UTF-8 String).

Plain UTF-8 keys are unaffected, which is presumably why this has gone unnoticed.

## Verification

Reproduces under **ordinary GC**, no `GC.stress` required — one corrupted key across 150k
iterations (0/50k, 0/50k, 1/50k), versus 100/100 with stress. That second number is an
existence proof rather than a rate.

Added regression tests to `ruby/tests/gc_test.rb` covering string keys, Symbol keys, and the
map-field kwarg path. Verified red/green against the same tree:

| ext build | new tests |
|---|---|
| unpatched `main` | 3 tests, **3 failures** |
| with this change | 3 tests, 300 assertions, **0 failures** |

Full Ruby suite green with the change on ruby 4.0.6 / arm64-darwin — `basic.rb` (133 tests,
157,864 assertions), `basic_proto2.rb` (93), `repeated_field_test.rb` (40),
`encode_decode_test.rb`, `memory_test.rb`, `object_cache_test.rb`, `well_known_types_test.rb`,
`service_test.rb`, `oom_test.rb`, `multi_level_nesting_test.rb` — 0 failures, 0 errors.

Reported separately via the channel in `SECURITY.md`, since this is a memory-safety issue in
an OT0 repository.

Closes #29026

COPYBARA_INTEGRATE_REVIEW=https://github.com/protocolbuffers/protobuf/pull/29026 from jeremy:ruby-map-key-use-after-free e11cc7dfe2
PiperOrigin-RevId: 961236703
2026-08-07 19:44:44 -07:00
..
basic.rb [Ruby]Implement #to_hash for message classes (#20866) 2025-03-28 19:18:16 -07:00
basic_proto2.rb Added a test that verifies existing enum behavior for Ruby. 2025-12-30 14:17:56 -08:00
basic_test.proto Implement edition 2023 support in all Ruby runtimes. 2024-03-15 15:17:33 -07:00
basic_test_features.proto Implement edition 2023 support in all Ruby runtimes. 2024-03-15 15:17:33 -07:00
basic_test_proto2.proto Implement edition 2023 support in all Ruby runtimes. 2024-03-15 15:17:33 -07:00
BUILD.bazel Handle ruby allocation failures gracefully 2026-07-28 21:08:09 -07:00
common_tests.rb Fix Ruby repeated enum const getter for unset fields (#27848) 2026-06-09 08:13:48 -07:00
encode_decode_test.rb Gracefully handle payloads >2GB. 2026-04-01 07:48:31 -07:00
gc_test.rb Ruby: fix use-after-free of map keys aliasing a temporary String (#29026) 2026-08-07 19:44:44 -07:00
generated_code.proto Auto capitalize enums name in Ruby (#10454) 2022-09-28 16:32:16 -04:00
generated_code_editions.proto Implement edition 2023 support in all Ruby runtimes. 2024-03-15 15:17:33 -07:00
generated_code_proto2.proto Auto capitalize enums name in Ruby (#10454) 2022-09-28 16:32:16 -04:00
generated_code_proto2_test.rb Set execute bit on files if and only if they begin with (#!). (#7347) 2020-04-01 15:28:25 -07:00
generated_code_test.rb Implement edition 2023 support in all Ruby runtimes. 2024-03-15 15:17:33 -07:00
golden-ruby_ffi_bindings.txt Add UPB_NODISCARD in many places, prioritizing ones that use the return value to indicate an error 2026-04-20 14:07:50 -07:00
implementation.rb Ruby FFI implementation (#13343) 2023-07-24 23:17:47 -07:00
memory_test.rb Add some missing tests to the test setting of Bazel for Ruby (#19870) 2025-01-30 19:41:27 -08:00
multi_level_nesting_test.proto Make the new multi-level ruby tests fit the existing structure better and add them to the makefile 2021-01-14 12:26:04 -08:00
multi_level_nesting_test.rb Fix jruby support to handle messages nested more than 1 level deep 2021-01-14 12:26:04 -08:00
object_cache_test.rb Moved ObjectCache into an internal module. 2024-02-13 09:34:09 -08:00
oom_test.rb Handle ruby allocation failures gracefully 2026-07-28 21:08:09 -07:00
repeated_field_test.proto Breaking Change: Dropped support for Ruby DSL, [as previously announced](https://engdoc.corp.google.com/eng/doc/devguide/proto/news/2023-12-27.md#ruby-breaking-changes). 2024-01-03 13:05:48 -08:00
repeated_field_test.rb Add test for RepeatedField concatenation and arena lifetime. 2026-07-07 05:59:28 -07:00
ruby_version.rb Remove all autotools usage (#10132) 2022-08-10 22:51:29 -07:00
service_test.proto Implement service & method descriptor lookup in Ruby (#15817) 2024-03-22 10:35:56 -07:00
service_test.rb Add to_proto to descriptor classes (#19971) 2025-02-04 10:54:52 -08:00
stress.proto Fix separate issues in JRuby's "native" dup and inspect methods. (#15265) 2024-01-05 10:41:01 -08:00
stress.rb Fix separate issues in JRuby's "native" dup and inspect methods. (#15265) 2024-01-05 10:41:01 -08:00
test_import.proto Added unit test for PascalCasing package names in Ruby. 2016-07-25 15:16:23 -07:00
test_import_proto2.proto Adds support for proto2 syntax for Ruby gem. 2018-09-27 14:21:16 -04:00
test_ruby_package.proto Fix Ruby module name generation when the ruby_package option is used (again) (#5794) 2019-03-02 10:38:10 -08:00
test_ruby_package_proto2.proto Fix Ruby module name generation when the ruby_package option is used (again) (#5794) 2019-03-02 10:38:10 -08:00
type_errors.rb Updated Ruby min version to 2.7, and removed some compat code 2023-07-11 11:32:03 -07:00
utf8.proto [Ruby] Warn if assigning a "UTF-8" string with invalid UTF-8. (#17253) 2024-07-17 08:15:14 -07:00
utf8.rb Error if assigning a "UTF-8" string with invalid UTF-8. 2024-10-31 13:47:55 -07:00
well_known_types_test.rb Bulk update to use assert_* methods wherever possible. (#13156) 2023-06-28 20:31:32 -07:00