[ldns-users] contrib/python: memory leak in ldns_pkt.new_query

Karel Slany karel.slany at nic.cz
Wed Aug 7 12:47:39 UTC 2013


Hello,

the patch in the attachment solves the memory leak and the owner problem.

Someone at NLnet Labs, please, apply the patch onto the ldns svn.


Using '%newobject ldns_pkt_query_new;' solved only one half of the
problem. Dname has to be cloned before being used in new_query because
ldns_pkt_query_new() uses the rr_name pointer directly instead of
creating a copy. Double free is the result as the same rr_name object is
being destroyed once in dname and second in pkt.

The patch creates a new wrapper function that automatically clones the
dname rr_name before calling ldns_pkt_query_new().

Best regards,
K.

Am 07.08.2013 11:26, schrieb Karel Slany:
> Hello Johannes,
> 
> I'll have look at it.
> 
> K.
> 
> Am 06.08.2013 19:59, schrieb Johannes Naab:
>> Hi,
>>
>> The python wrapper for ldns appears to leak memory when packets are
>> created by ldns_pkt.new_query.
>>
>> The following is a short example (tested in ldns-1.6.16, Python 2.7.5
>> Linux 3.10, amd64):
>> #!/usr/bin/env python2.7
>> import ldns
>>
>> dname = ldns.ldns_dname("test.nic.cz")
>>
>> def leak():
>>     pkt = ldns.ldns_pkt.new_query(dname, ldns.LDNS_RR_TYPE_A,
>> ldns.LDNS_RR_CLASS_IN, 0)
>>
>> while True:
>>     leak()
>>
>>
>> Root of the problem seems to be, that the pointer to the ldns_pkt struct
>> is not considered owned by the swig wrapper, and thus the pkt struct is
>> not freed upon garbage collection in python.
>>
>> import ldns
>> dname = ldns.ldns_dname("test.nic.cz")
>> pkt = ldns.ldns_pkt.new_query(dname, ldns.LDNS_RR_TYPE_A,
>>                               ldns.LDNS_RR_CLASS_IN, 0)
>> print pkt.thisown
>>
>> => False.
>>
>> If new_query is replaced by new_query_frm_str the packet is "owned" by
>> the swig wrapper, and the packet does not leak.
>>
>>
>> pkt = ldns.ldns_pkt.new_query_frm_str(str(dname), ldns.LDNS_RR_TYPE_A,
>>                                       ldns.LDNS_RR_CLASS_IN, 0)
>> print pkt.thisown
>>
>> => True
>>
>> I tried to convince swig that the struct should be owned by the wrapper
>> by inserting a "%newobject ldns_pkt_query_new;" below line 44 in
>> contrib/python/ldns_packet.i. This made swig own the pointer, but
>> freeing the packet on garbage collection made python crash (memory
>> corruption, double free). My knowledge and understanding of swig is limited.
>>
>>
>> Johannes
>> _______________________________________________
>> ldns-users mailing list
>> ldns-users at open.nlnetlabs.nl
>> http://open.nlnetlabs.nl/mailman/listinfo/ldns-users
>>
> 
> _______________________________________________
> ldns-users mailing list
> ldns-users at open.nlnetlabs.nl
> http://open.nlnetlabs.nl/mailman/listinfo/ldns-users
> 

-------------- next part --------------
Index: trunk/contrib/python/ldns_packet.i
===================================================================
--- trunk/contrib/python/ldns_packet.i	(revision 3860)
+++ trunk/contrib/python/ldns_packet.i	(working copy)
@@ -109,6 +109,18 @@
 
 /* clone data when pushed in */
 
+%newobject _ldns_pkt_query_new;
+%rename (__ldns_pkt_query_new) ldns_pkt_query_new;
+%inline
+%{
+  ldns_pkt * _ldns_pkt_query_new(ldns_rdf *rr_name, ldns_rr_type rr_type,
+      ldns_rr_class rr_class, uint16_t flags)
+  {
+    return ldns_pkt_query_new(ldns_rdf_clone(rr_name), rr_type, rr_class,
+             flags);
+  }
+%}
+
 %rename(__ldns_pkt_push_rr) ldns_pkt_push_rr;
 %inline %{
 bool _ldns_pkt_push_rr(ldns_pkt* p, ldns_pkt_section sec, ldns_rr *rr) {
@@ -170,7 +182,7 @@
                :param flags: packet flags
                :returns: new ldns_pkt object
             """
-            return _ldns.ldns_pkt_query_new(rr_name, rr_type, rr_class, flags)
+            return _ldns._ldns_pkt_query_new(rr_name, rr_type, rr_class, flags)
 
         @staticmethod
         def new_query_frm_str(rr_name, rr_type, rr_class, flags, raiseException = True):


More information about the ldns-users mailing list