Array Decoder Leaks Memory if Scalar Decoder Raises

Issue #279 resolved
Jeremy Evans
created an issue

pg uses xmalloc/free for the temporary buffer for array elements, without using rb_ensure to ensure that free is called if an exception is raised by decoding. This means if a decoder raises an error, the memory is leaked. Example test case:

require 'pg'
conn = PG.connect
class ByteaLeaker < PG::SimpleDecoder
  def decode(string, tuple=nil, field=nil)
    raise
  end
end
PG::BasicTypeRegistry.register_type(0, 'bytea', nil, ByteaLeaker)
map = conn.type_map_for_results = PG::BasicTypeMapForResults.new(conn)
GC.start
system("ps aux -p #{$$}")
100.times do
  print '.'
  res = nil
  begin
    res = conn.exec("SELECT ARRAY['#{'a'*1000000}']::bytea[]")
    res.getvalue(0,0)
  rescue RuntimeError
  ensure
    res.clear if res
  end
end
GC.start
system("ps aux -p #{$$}")

FYI, sequel_pg uses rb_str_buf_new for the temporary buffer to avoid needing rb_ensure, but make sure you use RB_GC_GUARD appropriately if you do that.

There is a similar leak in pg_text_dec_bytea, where the buffer returned by PQunescapeBytea is leaked if rb_tainted_str_new fails. That's harder to hit, but if you are out of memory, leaking memory is going to make things worse. sequel_pg now uses rb_ensure in that case to avoid leaking memory.

Comments (2)

  1. Log in to comment