Skip to content

Commit 6b19d54

Browse files
committed
prevent infinite loop from packing circular refernce objects
1 parent 0e8c588 commit 6b19d54

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed

lib/async/container/supervisor/message_wrapper.rb

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# Copyright, 2025, by Samuel Williams.
55

66
require "msgpack"
7+
require "set"
78

89
module Async
910
module Container
@@ -29,7 +30,7 @@ def read
2930

3031
def pack(message)
3132
@packer.clear
32-
normalized_message = normalize(message)
33+
normalized_message = normalize(message, Set.new)
3334
@packer.pack(normalized_message)
3435
@packer.full_pack
3536
end
@@ -40,15 +41,27 @@ def unpack(data)
4041

4142
private
4243

43-
def normalize(obj)
44+
def normalize(obj, visited = Set.new.compare_by_identity)
45+
# Check for circular references
46+
return "..." if visited.include?(obj)
47+
4448
case obj
4549
when Hash
46-
obj.transform_values{|v| normalize(v)}
50+
visited.add(obj)
51+
result = obj.transform_values{|v| normalize(v, visited)}
52+
visited.delete(obj)
53+
result
4754
when Array
48-
obj.map{|v| normalize(v)}
55+
visited.add(obj)
56+
result = obj.map{|v| normalize(v, visited)}
57+
visited.delete(obj)
58+
result
4959
else
5060
if obj.respond_to?(:as_json) && (as_json = obj.as_json) && as_json != obj
51-
normalize(obj.as_json)
61+
visited.add(obj)
62+
result = normalize(as_json, visited)
63+
visited.delete(obj)
64+
result
5265
else
5366
obj
5467
end

test/async/container/message_wrapper.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ def read_message
4141
Integer.send(:remove_method, :as_json)
4242
end
4343

44+
it "normalizes circular references" do
45+
array = []
46+
array << array
47+
48+
write_message({data: array})
49+
50+
result = read_message
51+
expect(result[:data]).to be == ["..."]
52+
end
53+
4454
it "handles simple strings" do
4555
write_message({message: "hello world"})
4656

0 commit comments

Comments
 (0)