summaryrefslogtreecommitdiff
path: root/weakmap.c
AgeCommit message (Collapse)Author
2024-10-29Remove double semicolon at end of line in weakmap.c [ci skip]Peter Zhu
2024-10-24Fix potential issue in wmap_cmp for dead entriesPeter Zhu
wmap_cmp will always return true for dead entries since dead entries have a Qundef value. When the keys have a hash collision, this may cause the wrong entry to be returned. This commit changes wmap_cmp to compare by weakmap_entry pointers when the entry is dead. Notes: Merged: https://github.com/ruby/ruby/pull/11944
2024-10-21Fix wmap_compact from potentially corrupting ST tablePeter Zhu
Since we hash by the address of the key in the WeakMap, we cannot change the key in the same entry because deleting the key could require hashing. This commit changes it to allocate and insert a new entry if the key has moved. Notes: Merged: https://github.com/ruby/ruby/pull/11924
2024-10-21[DOC] Fix indentation of comment in wmap_compact_table_iPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11924
2024-10-09Fix spellingJohn Bampton
Notes: Merged: https://github.com/ruby/ruby/pull/11835
2024-08-23Fix use-after-free in WeakKeyMap#clearPeter Zhu
[Bug #20691] If the WeakKeyMap has been marked but sweeping hasn't started yet and we cann WeakKeyMap#clear, then there could be a use-after-free because we do not call rb_gc_remove_weak to remove the key from the GC. For example, the following code triggers use-after-free errors in Valgrind: map = ObjectSpace::WeakKeyMap.new 1_000.times do 1_000.times do map[Object.new] = nil end map.clear end Output from Valgrind: ==61230== Invalid read of size 8 ==61230== at 0x25CAF8: gc_update_weak_references (default.c:5593) ==61230== by 0x25CAF8: gc_marks_finish (default.c:5641) ==61230== by 0x26031C: gc_marks_continue (default.c:5987) ==61230== by 0x26031C: gc_continue (default.c:2255) ==61230== by 0x2605FC: newobj_cache_miss (default.c:2589) ==61230== by 0x26111F: newobj_alloc (default.c:2622) ==61230== by 0x26111F: rb_gc_impl_new_obj (default.c:2701) ==61230== by 0x26111F: newobj_of (gc.c:890) ==61230== by 0x26111F: rb_wb_protected_newobj_of (gc.c:917) ==61230== by 0x2DE218: rb_class_allocate_instance (object.c:131) ==61230== by 0x2E32A8: class_call_alloc_func (object.c:2141) ==61230== by 0x2E32A8: rb_class_alloc (object.c:2113) ==61230== by 0x2E32A8: rb_class_new_instance_pass_kw (object.c:2172) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) ==61230== Address 0x2159cb00 is 0 bytes inside a block of size 8 free'd ==61230== at 0x4849B2C: free (vg_replace_malloc.c:989) ==61230== by 0x248EF1: rb_gc_impl_free (default.c:8512) ==61230== by 0x248EF1: rb_gc_impl_free (default.c:8493) ==61230== by 0x248EF1: ruby_sized_xfree.constprop.0 (gc.c:4178) ==61230== by 0x4627EC: wkmap_free_table_i (weakmap.c:652) ==61230== by 0x3A54AF: apply_functor (st.c:1633) ==61230== by 0x3A54AF: st_general_foreach (st.c:1543) ==61230== by 0x3A54AF: rb_st_foreach (st.c:1640) ==61230== by 0x46203C: wkmap_clear (weakmap.c:973) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) ==61230== Block was alloc'd at ==61230== at 0x484680F: malloc (vg_replace_malloc.c:446) ==61230== by 0x25C68E: rb_gc_impl_malloc (default.c:8527) ==61230== by 0x4622E9: wkmap_aset_replace (weakmap.c:817) ==61230== by 0x3A4D02: rb_st_update (st.c:1487) ==61230== by 0x4623E4: wkmap_aset (weakmap.c:854) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) ==61230== ==61230== Invalid write of size 8 ==61230== at 0x25CB3B: gc_update_weak_references (default.c:5598) ==61230== by 0x25CB3B: gc_marks_finish (default.c:5641) ==61230== by 0x26031C: gc_marks_continue (default.c:5987) ==61230== by 0x26031C: gc_continue (default.c:2255) ==61230== by 0x2605FC: newobj_cache_miss (default.c:2589) ==61230== by 0x26111F: newobj_alloc (default.c:2622) ==61230== by 0x26111F: rb_gc_impl_new_obj (default.c:2701) ==61230== by 0x26111F: newobj_of (gc.c:890) ==61230== by 0x26111F: rb_wb_protected_newobj_of (gc.c:917) ==61230== by 0x2DE218: rb_class_allocate_instance (object.c:131) ==61230== by 0x2E32A8: class_call_alloc_func (object.c:2141) ==61230== by 0x2E32A8: rb_class_alloc (object.c:2113) ==61230== by 0x2E32A8: rb_class_new_instance_pass_kw (object.c:2172) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) ==61230== Address 0x2159cb00 is 0 bytes inside a block of size 8 free'd ==61230== at 0x4849B2C: free (vg_replace_malloc.c:989) ==61230== by 0x248EF1: rb_gc_impl_free (default.c:8512) ==61230== by 0x248EF1: rb_gc_impl_free (default.c:8493) ==61230== by 0x248EF1: ruby_sized_xfree.constprop.0 (gc.c:4178) ==61230== by 0x4627EC: wkmap_free_table_i (weakmap.c:652) ==61230== by 0x3A54AF: apply_functor (st.c:1633) ==61230== by 0x3A54AF: st_general_foreach (st.c:1543) ==61230== by 0x3A54AF: rb_st_foreach (st.c:1640) ==61230== by 0x46203C: wkmap_clear (weakmap.c:973) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) ==61230== Block was alloc'd at ==61230== at 0x484680F: malloc (vg_replace_malloc.c:446) ==61230== by 0x25C68E: rb_gc_impl_malloc (default.c:8527) ==61230== by 0x4622E9: wkmap_aset_replace (weakmap.c:817) ==61230== by 0x3A4D02: rb_st_update (st.c:1487) ==61230== by 0x4623E4: wkmap_aset (weakmap.c:854) ==61230== by 0x4296BC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3788) ==61230== by 0x44A9CD: vm_sendish (vm_insnhelper.c:5955) ==61230== by 0x44A9CD: vm_exec_core (insns.def:898) ==61230== by 0x43A0E4: rb_vm_exec (vm.c:2564) ==61230== by 0x2341B4: rb_ec_exec_node (eval.c:281) ==61230== by 0x236258: ruby_run_node (eval.c:319) ==61230== by 0x15D665: rb_main (main.c:43) ==61230== by 0x15D665: main (main.c:62) Co-authored-by: Jean Boussier <[email protected]> Notes: Merged: https://github.com/ruby/ruby/pull/11437
2024-08-23[DOC] Fix typos in ObjectSpace::WeakMap docsAndy Wong
The value of variable key2 should be "bar". This way, when nil is assigned to val1 and garbage collection occurs, the output of m.keys will then be ["bar"]. Notes: Merged: https://github.com/ruby/ruby/pull/11442 Merged-By: nobu <[email protected]>
2024-08-22Fix use-after-free for WeakKeyMapPeter Zhu
[Bug #20688] We cannot free the key before the ST_DELETE because it could hash the key which would read the key and would cause a use-after-free. Instead, we store the key and free it on the next iteration. Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Fix WeakMap use-after-freePeter Zhu
[Bug #20688] We cannot free the weakmap_entry before the ST_DELETE because it could hash the key which would read the weakmap_entry and would cause a use-after-free. Instead, we store the entry and free it on the next iteration. For example, the following script triggers a use-after-free in Valgrind: weakmap = ObjectSpace::WeakMap.new 10_000.times { weakmap[Object.new] = Object.new } ==25795== Invalid read of size 8 ==25795== at 0x462297: wmap_cmp (weakmap.c:165) ==25795== by 0x3A2B1C: find_table_bin_ind (st.c:930) ==25795== by 0x3A5EAA: st_general_foreach (st.c:1599) ==25795== by 0x3A5EAA: rb_st_foreach (st.c:1640) ==25795== by 0x25C991: gc_mark_children (default.c:4870) ==25795== by 0x25C991: gc_marks_wb_unprotected_objects_plane (default.c:5565) ==25795== by 0x25C991: rgengc_rememberset_mark_plane (default.c:5557) ==25795== by 0x25C991: rgengc_rememberset_mark (default.c:6233) ==25795== by 0x25C991: gc_marks_start (default.c:6057) ==25795== by 0x25C991: gc_marks (default.c:6077) ==25795== by 0x25C991: gc_start (default.c:6723) ==25795== by 0x260F96: heap_prepare (default.c:2282) ==25795== by 0x260F96: heap_next_free_page (default.c:2489) ==25795== by 0x260F96: newobj_cache_miss (default.c:2598) ==25795== by 0x26197F: newobj_alloc (default.c:2622) ==25795== by 0x26197F: rb_gc_impl_new_obj (default.c:2701) ==25795== by 0x26197F: newobj_of (gc.c:890) ==25795== by 0x26197F: rb_wb_protected_newobj_of (gc.c:917) ==25795== by 0x2DEA88: rb_class_allocate_instance (object.c:131) ==25795== by 0x2E3B18: class_call_alloc_func (object.c:2141) ==25795== by 0x2E3B18: rb_class_alloc (object.c:2113) ==25795== by 0x2E3B18: rb_class_new_instance_pass_kw (object.c:2172) ==25795== by 0x429DDC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3786) ==25795== by 0x44B08D: vm_sendish (vm_insnhelper.c:5953) ==25795== by 0x44B08D: vm_exec_core (insns.def:898) ==25795== by 0x43A7A4: rb_vm_exec (vm.c:2564) ==25795== by 0x234914: rb_ec_exec_node (eval.c:281) ==25795== Address 0x21603710 is 0 bytes inside a block of size 16 free'd ==25795== at 0x4849B2C: free (vg_replace_malloc.c:989) ==25795== by 0x249651: rb_gc_impl_free (default.c:8527) ==25795== by 0x249651: rb_gc_impl_free (default.c:8508) ==25795== by 0x249651: ruby_sized_xfree.constprop.0 (gc.c:4178) ==25795== by 0x4626EC: ruby_sized_xfree_inlined (gc.h:277) ==25795== by 0x4626EC: wmap_free_entry (weakmap.c:45) ==25795== by 0x4626EC: wmap_mark_weak_table_i (weakmap.c:61) ==25795== by 0x3A5CEF: apply_functor (st.c:1633) ==25795== by 0x3A5CEF: st_general_foreach (st.c:1543) ==25795== by 0x3A5CEF: rb_st_foreach (st.c:1640) ==25795== by 0x25C991: gc_mark_children (default.c:4870) ==25795== by 0x25C991: gc_marks_wb_unprotected_objects_plane (default.c:5565) ==25795== by 0x25C991: rgengc_rememberset_mark_plane (default.c:5557) ==25795== by 0x25C991: rgengc_rememberset_mark (default.c:6233) ==25795== by 0x25C991: gc_marks_start (default.c:6057) ==25795== by 0x25C991: gc_marks (default.c:6077) ==25795== by 0x25C991: gc_start (default.c:6723) ==25795== by 0x260F96: heap_prepare (default.c:2282) ==25795== by 0x260F96: heap_next_free_page (default.c:2489) ==25795== by 0x260F96: newobj_cache_miss (default.c:2598) ==25795== by 0x26197F: newobj_alloc (default.c:2622) ==25795== by 0x26197F: rb_gc_impl_new_obj (default.c:2701) ==25795== by 0x26197F: newobj_of (gc.c:890) ==25795== by 0x26197F: rb_wb_protected_newobj_of (gc.c:917) ==25795== by 0x2DEA88: rb_class_allocate_instance (object.c:131) ==25795== by 0x2E3B18: class_call_alloc_func (object.c:2141) ==25795== by 0x2E3B18: rb_class_alloc (object.c:2113) ==25795== by 0x2E3B18: rb_class_new_instance_pass_kw (object.c:2172) ==25795== by 0x429DDC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3786) ==25795== by 0x44B08D: vm_sendish (vm_insnhelper.c:5953) ==25795== by 0x44B08D: vm_exec_core (insns.def:898) ==25795== by 0x43A7A4: rb_vm_exec (vm.c:2564) ==25795== Block was alloc'd at ==25795== at 0x484680F: malloc (vg_replace_malloc.c:446) ==25795== by 0x25CE9E: rb_gc_impl_malloc (default.c:8542) ==25795== by 0x462A39: wmap_aset_replace (weakmap.c:423) ==25795== by 0x3A5542: rb_st_update (st.c:1487) ==25795== by 0x462B8E: wmap_aset (weakmap.c:452) ==25795== by 0x429DDC: vm_call_cfunc_with_frame_ (vm_insnhelper.c:3786) ==25795== by 0x44B08D: vm_sendish (vm_insnhelper.c:5953) ==25795== by 0x44B08D: vm_exec_core (insns.def:898) ==25795== by 0x43A7A4: rb_vm_exec (vm.c:2564) ==25795== by 0x234914: rb_ec_exec_node (eval.c:281) ==25795== by 0x2369B8: ruby_run_node (eval.c:319) ==25795== by 0x15D675: rb_main (main.c:43) ==25795== by 0x15D675: main (main.c:62) Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Remove wmap_free_entryPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Refactor wmap_compact to use wmap_foreachPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Use wmap_foreach for wmap_markPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Refactor wmap_foreach to pass weakmap_entryPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-22Add struct weakmap_entry for WeakMap entriesPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11421
2024-08-21Remove unused weakmap field in wmap_foreachPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/11413
2024-05-06[DOC] Fix typos in WeakMap docsEarlopain
2024-04-18chore: remove repetitive words (#10573)careworry
Signed-off-by: careworry <[email protected]>
2024-02-14Remove duplicated include in weakmap.cPeter Zhu
2024-02-12Replace assert with RUBY_ASSERT in weakmap.cPeter Zhu
assert does not print the bug report, only the file and line number of the assertion that failed. RUBY_ASSERT prints the full bug report, which makes it much easier to debug.
2024-01-30Use `UNDEF_P`Nobuyoshi Nakada
2023-12-14[DOC] Enhance docs for WeakMap and WeakKeyMap (#9160)Victor Shepelev
Enhance docs for WeakMap and WeakKeyMap * WeakKeyMap: more class-level explanations, more details on #getkey, fix a slight bug in code of #delete example; * WeekMap: a bit more detailed class- and method-level docs.
2023-12-12Make WeakKeyMap safe for compaction during allocationPeter Zhu
During allocation, the table may not have been allocated yet which would crash in the st_foreach.
2023-12-12Make WeakMap safe for compaction during allocationPeter Zhu
During allocation, the table may not have been allocated yet which would crash in the st_foreach.
2023-11-21Implement WeakKeyMap on VWAPeter Zhu
Benchmark: ``` puts(Benchmark.measure do 10_000_000.times do ObjectSpace::WeakKeyMap.new end end) ``` Before: ``` 2.554772 0.014167 2.568939 ( 2.575763) ``` After: ``` 1.994920 0.013583 2.008503 ( 2.012139) ```
2023-11-21Implement WeakMap on VWAPeter Zhu
Benchmark: ``` puts(Benchmark.measure do 10_000_000.times do ObjectSpace::WeakMap.new end end) ``` Before: ``` 2.568662 0.014001 2.582663 ( 2.601743) ``` After: ``` 2.025523 0.008676 2.034199 ( 2.041760) ```
2023-09-06Fix crash in WeakMap during compactionPeter Zhu
WeakMap can crash during compaction because the st_insert could allocate memory.
2023-09-05Introduce rb_gc_remove_weakPeter Zhu
If we're during incremental marking, then Ruby code can execute that deallocates certain memory buffers that have been called with rb_gc_mark_weak, which can cause use-after-free bugs. Notes: Merged: https://github.com/ruby/ruby/pull/8375
2023-09-05Reuse allocated buffer in WeakMapPeter Zhu
If the key exists in WeakMap and WeakKeyMap, then we can reuse the buffer and we can avoid an allocation. Notes: Merged: https://github.com/ruby/ruby/pull/8375
2023-08-25Implement WeakKeyMap using weak referencesPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/8113
2023-08-25Implement WeakMap using weak referencesPeter Zhu
Notes: Merged: https://github.com/ruby/ruby/pull/8113
2023-08-15[DOC] Improve doc guide compliance (#8221)Burdette Lamar
2023-04-15Implement ObjectSpace::WeakMap#delete and ObjectSpace::WeakKeyMap#deleteJean Boussier
[Feature #19561] It's useful to be able to remove references from weak maps. Notes: Merged: https://github.com/ruby/ruby/pull/7629
2023-04-15Add specs for ObjectSpace::WeakKeyMapJean Boussier
[Feature #18498] Notes: Merged: https://github.com/ruby/ruby/pull/7716
2023-03-17ObjectSpace::WeakMap: clean inverse reference when an entry is re-assignedJean Boussier
[Bug #19531] ```ruby wmap[1] = "A" wmap[1] = "B" ``` In the example above, we need to remove the `"A" => 1` inverse reference so that when `"A"` is GCed the `1` key isn't deleted. Notes: Merged: https://github.com/ruby/ruby/pull/7540
2023-03-16Fix incorrect size of WeakMap bufferPeter Zhu
In wmap_final_func, j is the number of elements + 1 (since j also includes the length at the 0th index), so we should resize the buffer to size j and the new length is j - 1. Notes: Merged: https://github.com/ruby/ruby/pull/7536
2023-03-14Fix crash during compactionPeter Zhu
[Bug #19529] The fix for [Bug #19529] in commit 548086b contained a bug that crashes on the following script: ``` wm = ObjectSpace::WeakMap.new obj = Object.new 100.times do wm[Object.new] = obj GC.start end GC.compact ```
2023-03-14ObjectSpace::WeakMap: fix compaction supportJean Boussier
[Bug #19529] `rb_gc_update_tbl_refs` can't be used on `w->obj2wmap` because it's not a `VALUE -> VALUE` table, but a `VALUE -> VALUE *` table, so we need some dedicated iterator. Notes: Merged: https://github.com/ruby/ruby/pull/7518
2023-03-10Mark weak maps as write barrier protectedJean Boussier
For both we mark the lambda finalizer. ObjectSpace::WeakMap doesn't mark any other reference, so we can just add the flag. ObjectSpace::WeakKeyMap only ever add new refs in `wkmap_aset`, so we can just trigger the write barrier there. Notes: Merged: https://github.com/ruby/ruby/pull/7498
2023-03-10Move WeakMap and WeakKeyMap code to weakmap.cPeter Zhu
These classes don't belong in gc.c as they're not actually part of the GC. This commit refactors the code by moving all the code into a weakmap.c file. Notes: Merged: https://github.com/ruby/ruby/pull/7451