ruby / psych

A libyaml wrapper for Ruby
MIT License
564 stars 203 forks source link

Guard from memory leak in Psych::Emitter#start_document #684

Closed peterzhu2118 closed 1 month ago

peterzhu2118 commented 1 month ago

When an exception is raised, it can leak memory in head. There are two places that can leak memory:

  1. Check_Type(tuple, T_ARRAY) can leak memory if tuple is not an array.
  2. StringValue(name) and StringValue(value) if they are not strings and the call to to_str does not return a string.

This commit fixes these memory leaks by wrapping the code around a rb_ensure so that the memory is freed in all cases.

The following code demonstrates the memory leak:

emitter = Psych::Emitter.new(StringIO.new)
nil_to_string_tags = [[nil, "tag:TALOS"]] + ([1] * 1000)
expected_array_tags = [1] * 1000

10.times do
  1_000.times do
    # Raises `no implicit conversion of nil into String`
    emitter.start_document([], nil_to_string_tags, 0)
  rescue TypeError
  end

  1_000.times do
    # Raises `wrong argument type Integer (expected Array)`
    emitter.start_document([], expected_array_tags, 0)
  rescue TypeError
  end

  puts `ps -o rss= -p #{$$}`
end

Before:

47248
79728
111968
144224
176480
208896
241104
273280
305472
337664

After:

14832
15088
15344
15344
15360
15632
15632
15632
15648
15648