Memory Leak with Async Code
Opened this issue · 5 comments
Hey folks,
I'm currently debugging a memory leak in mitmproxy which I think is rooted somewhere outside of our codebase. In short:
- We see a slow but unbounded (heap) memory increase in long-running mitmdump sessions.
len(gc.get_objects())
is constant over time.- When dumping the heap of a process that has accumulated a lot of memory, I see lots of certificate-related strings and lots of
_lib.c
1.
cat heap.bin | strings | sort | uniq -c | sort -r
37667 _lib.c 28694 B0@0 11262 DigiCert Inc1 8233 QuoVadis Limited1 7636 c0a0 7621 www.digicert.com1 0 7160 digicert inc1 6348 www.digicert.com1$0" 6207 GlobalSign 6206 GlobalSign1 5598 Amazon1 5509 Google Trust Services LLC1 5508 US1"0 5331 Salford1 5328 Greater Manchester1 5070 Texas1 5066 Houston1 4404 h&(z[ 4331 globalsign1 4227 Panama1 4121 Arizona1 4040 quovadis limited1 4006 GlobalSign nv-sa1 3854 Entrust, Inc.1(0& 3853 See www.entrust.net/legal-terms1907 3829 Illinois1 3827 Chicago1!0 3799 TrustCor Certificate Authority1 3797 Panama City1$0" 3796 TrustCor Systems S. de R.L.1'0% 3624 Budapest1 3606 houston1 3601 DigiCert Inc 3564 AffirmTrust1 3495 Starfield Technologies, Inc.1200 3490 salford1 3489 www.digicert.com 3459 google trust services llc1 3449 globalsign 3434 texas1 3409 greater manchester1 3405 .1.1 3345 Dhimyotis1 3332 US1!0 3321 amazon1 3315 GlobalSign0 3183 us1"0 3139 130801120000Z 2993 nykiad 2827 Scottsdale1%0# 2767 The USERTRUST Network1.0, 2767 New Jersey1 2767 Jersey City1 2744 Root CA1 2652 WISeKey1"0 2651 trustcor certificate authority1 2650 panama city1$0" 2650 OISTE Foundation Endorsed1(0& 2647 trustcor systems s. de r.l.1'0% 2636 US1 0 2633 SecureTrust Corporation1 2612 0000Z 2610 chicago1!0 2601 TAIWAN-CA1 2576 arizona1 2573 budapest1 2571 SSL Corporation110/ 2552 COMODO CA Limited1+0) 2552 Hong Kong1 2547 DE1+0) 2545 T-Systems Trust Center1%0# 2544 "T-Systems Enterprise Services GmbH1 2536 0L1 0 2531 5959Z 2511 see www.entrust.net/legal-terms1907 2508 Trustwave Holdings, Inc.1:08 2506 emSign PKI1 2484 globalsign nv-sa1 2483 illinois1 2479 ;Hellenic Academic and Research Institutions Cert. Authority1@0> 2473 180218183000Z 2470 Hongkong Post1 0 2462 160622000000Z 2453 150526000000Z 2452 panama1 2452 PL1"0 2448 Unizeto Technologies S.A.1'0% 2446 Buypass AS-9831633271 0 2434 emSign PKI1%0# 2388 PL1!0 2387 Certum Certification Authority1 2386 Asseco Data Systems S.A.1'0% 2377 Athens1D0B 2362 Microsoft Corporation1604 2350 FNMT-RCM1 2341 affirmtrust1 2257 US1%0# 2243 ertification Authority 2234 entrust, inc.1(0& 2172 .1&0$ 2168 0002 481463081000361 2138 www.digicert.com1!0 2137 im Teknolojileri ve Hizmetleri A. 2121 Inc 2104 www.digicert.com1+0) 2086 The Go Daddy Group, Inc.110/ 2080 QuoVadis Limited 2054 www.xrampsecurity.com1$0" 2049 %bw+s 2002 061110000000Z 1910 Amazon 1859 oiste foundation endorsed1(0& 1858 wisekey1"0 1840 090901000000Z 1830 scottsdale1%0# 1796 Google Trust Services LLC 1783 ssl corporation110/ 1776 root ca1 1768 t-systems trust center1%0# 1767 starfield technologies, inc.1200 1765 G0E1 1760 securetrust corporation1 1753 certum certification authority1 1749 jersey city1 1748 the usertrust network1.0, 1738 asseco data systems s.a.1'0% 1738 Salford 1738 Greater Manchester 1736 "t-systems enterprise services gmbh1 1731 .edu0 1730 trustwave holdings, inc.1:08 1729 ;hellenic academic and research institutions cert. authority1@0> 1724 hong kong1 1723 taiwan-ca1 1719 unizeto technologies s.a.1'0% 1718 hongkong post1 0 1711 ra EBG Bili 1711 comodo ca limited1+0) 1697 dhimyotis1 1695 380118235959Z0 1695 .eu0 1694 athens1D0B 1694 .gr0 1681 emsign pki1 1677 cert.com 1676 microsoft corporation1604 1670 Certigna 1667 buypass as-9831633271 0 1665 Houston 1664 emSign PKI [...]
./sample.py heap.bin
(random chunks from the heap)
root@memtest ~# ./sample.py heap.bin === 5b83400 === b'\r\x00\x00\x00\x17\x00\x00\x00\xa0.\xdd\x1f\x94U\x00\x00@\x00\x00\x00\x00\x0 0\x00\x00!\x00\x00\x00\x00\x00\x00\x00\x80\x15\xdd\x1f\x94U\x00\x00\x06\x00\x0 0\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00!\x00\x00\x00\x00\x00\x00 \x00\x01\x00\x00\x00\x02\x00\x00\x000\xff\xdc\x1f\x94U\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00!\x00\x00\x00\x00\x00\x00\x00\xd0\n\xdd\x1f\x94U\x00\x00\x01\x 00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00q\x00\x00\x00\x00\x0 0\x00\x000J1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x120\x10\x06\x03U\x04\n\x13\tI denTrust1\'0%\x06\x03U\x04\x03\x13\x1eIdenTrust Commercial Root CA 1\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00A\x00\x00\x00\x00\x00\x00\x00s)\xc5\xccj\x19\xec\xecz\ xa7\xb0H\xb2\r\x1aX\xdf-7\xf4\x81Mc\xc7\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff!\xef\x9cF\x91U\x00\x 00!\x00\x00\x00\x00\x00\x00\x00EC\x00\xb4g\x7f\x00\x00\x90\x04\xdd\x1f\x94U\x0 0\x00\x00\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00 \x00\x00\x00\x00\x00\xf0e\xfc$\x94U\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x0 0\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x00\x0 0\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xfe\ xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\ xc1\x00\x00\x00\x00\x00\x00\x00\x00\x830\xb2g\x7f\x00\x00P\x0f\xdd\x1f\x94U\x0 0\x00\x90\x0b\xdd\x1f\x94U\x00\x00`\x14\xdd\x1f\x94U\x00\x00\xcb\x02\x00\x00\x 01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00P\xff\xdc\x1f\x94U\x00\x00\x14\x 00\x00\x00\x00\x00\x00\x00 \x14\xdd\x1f\x94U\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x13\xd d\x1f\x94U\x00\x00@\x13\xdd\x1f\x94U\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x 80\x16\xdd\x1f\x94U\x00\x00\x10\x00\xdd\x1f\x94U\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x00\xe0\x0c\xdd\x1f\x94U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00\x00q\x00\x00\x00\x00\x00\x00\x00\x80\x01\x00\x00g\x7f\x00\x00`\x07\xdd \x1f\x94U\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\xa0\x07\xdd\x1f\x94U\x00\x00\x06\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x11\x01\x00\x00\x00\x00 \x00\x00\x87\x0b\x8b\x80\x8a\x19\xb3\xf2[\x83M\\\xa3-\x8 b\x95!\x18B+N\xf2\x1d>P\xb90\x9d\xd6}\xef\xf1,H\xcb\xfa\xe0\xa6n \xfc\xa5h@\x8 2\xe9\xc6\xbb\xf1r\x02\x90\xbbD\x0c\xe5\xf7\xe7;R \xf8\xa4\xca\xeft\xaf7\x84@y U\xe6V\xf0\xda\x92\xb2\xfc_<\x12)\xca\n\x1a\xbdG>\x90\xcb\xc9\xee8\x8er\x80\x1 b\xf0S\x8bw\x97p\x8f O\xab\xa2\xdax\xcbp(O\xa8\xe2!\xcd\xceF\x95\xd8kB;u\xf7\x fc\x12\x8c{\xe6\x8aZ(\x7f\x00\xa5\xd5\xd7n\x90|\x82\xa2\xdf\x9c\x1cn\x18\xa9\x cbU\xf8\xe5\x16iJb"h\xf3On\x98H\xdc\x94Eg>|\xe7R\xa0A\xc4 \xc8\xa0!\x93@!)\x92 \xdf\x0e\xce0\xa4~\xdbR\xf1\xff{ \x0c\xa6\xb3Co\x15\xc5\x16\xc0\xb6\x8638\x15k o\xf6\xbf\x8d\xcf\xe1A\x82\x10M{\x16\xe0\x8f\xe6\xc5=\x04\xf5\xed\xff\x94b+|\x 1cU}\xff\xc1_2\xe6\x06*\xa57\x84>F\xd1\xf3x\x14\xf2\xd4\x80\xb5S\x86#\xbez\xc8 \x92>\x88;$\xa0S)\x98h\x1d\xbch\xed}I\xf4\xf10\xaf\xdd\xcc\x19\x81}\xd1f\xe1?\ xa3\xe5 #\xc0\x06\x0e\x829\x06\xf5\xceu\xba\xd3\x1a\x84\xc6\xbd\xd4\x88\xdfOxT \x91n\xdf>\xe0\xe1B\xdd\x1e\\\xfeY\xdfb\\\xa5\x8c>6\xd72\xd0\xecJk\xeb\x05\x1d |\xa5\x8f\x80\x19\x9bg\x8e!\x9b@\x97\x15%\xb7\x9b\xdf\x17\xb4\x8d\xa8\xe6\\\xa f\xf8\xdd(\xaa\x81\x14\xb8~\x81{\x90\xd7U\xc4\xe5\xfb!7|\x96\xc3\xe5\x193c\xa7 U\xd4\xbf\x1dn\xdc0\xc1\xec\x13\xfe7o0\xa7\x85T\xd2!\x7f*\xbf;jF\x00zw\xa2,\xc 1-\x04\xcdN\xaa*Q\xb8\x9f*\xf6\x9f/*\xbb\xb9\xca^zu|\x06q\xc4lA\xc7F\xd6\xcb[\ xc5\x13zb\xbe\\\xe1\x96\xbdy5I{i\xd1\xd3\xd3\xf3\xc1\xdf\xf9\x7fR\x95\xcc\x9d\ x9b#\xcai7&\xb4\x93\xec\xec\x7f\xa1r\xfc\xe0\x87o\xaa\xd7\x19\xcd#\xf7\x80\xf3 K\x80\x8f\x8a\x16Q\xe6\xc1\rG$Eh\xedJ\xd9\xafk\x9f\x84\xd6,\xe4T\xcf.\xaf\x06\ x12\n5\x95>\xa82@\xb7\x001\xcfH\x8a\xc7\xe6J\n\xb9\x139\x08\xdc\x03E\x03\xe9\x c0I=\xad\x14SDNS\xe1\x1d\x86\x9e.]|m\xa3\xee\xf0\xb2\xb8\xcdQ\x97\x07\xe3\xf56 \xe8\xe1{z\xb2\x98\xf8\xa5c1\x9a\x82\xd8\x10\xceB\xb0\x86\xca]548\xc5\x00\xbe\ xda\x18B\xd6`\x93\x16r\x9e\xba2Qw\xfa\x93\xbe\xc5\x9d\x7f\xc5\xd9\x9b\xb7\\;\x 03\x0c\x9b\xc2&@\x14/i08\x8b\xc2\xb8\x8d\xf6\xb3>\xef\x18\x82\x15\xe0\x1d\xfe\ xcfz-\xd0V\xd0\x8f\xb9j\x9e\xcd\x9ah\xb0\x08\xeeK\x0f\xfe0\xf4\xc8\x08\xc0\xa0 k"U\xe7\xda\xbd>\xc7\xb9R\xbc\xc9?\x81\xbeg\x9a\xc5\xe7\xd4 }\xa8\x81\xb0\xa4\ xca\xd3#\xbb\x8bb\nG_\x1a^&\xe8#7\x17\x17|\xb3\t(\xfd\xe9-jDS%\xef\xdf<\xe4p3\ xe6i\xf2\xe7*_\xf2\x18cqn\x9eFf\xe2\x03c\tJ\xd8\xb2\x1ci\xfc\xc0\xcc\x1bt\xad\ xd7\xc72N\xf6l\x8c@:\xaa\x14\x83"\x8c\xee\xed\x82\x93\xe3\x1e\x8b\x919lb\xe2\x b6\xa48\xb6\x90\xe1\x1d\x82U\xb1t;3.y\xc3\xef\xfc\x154\xc1\x03P^\xe3\xf3\xb0\x c9Y;\xac\xdd\xbe\x93\x9d\xc1z\xa8&\xde7\x079D\xf0Y((\xe1\xc71\x85Zy\xd01\x01\x f6\xf8\xce\xb6\xa8\x81\xae\x1a%z\xff(\xa5\xe5\x0b\xe3\x13\x18\xc2h\xa5\xcc;{`+ \xbe-\xd22U\xfd\xff!oX\r/\x9d\x94J\xa5Uo\xba8T\xec\xbaP\x96\x81p\xb8\x82\xe8\x d0r/y\xe3\xd2t#\xe0=\xf8\x82\xa5c\x89x\x1b\xfb\x14rI\xaf\x19\x04\x08[?\xcc\xc8 k\x08\xcbir\x08\x96\xf8\xeb%)\xab]\x1e\xa2\n\xc7\xbb\xbbe\xaaX\xb6E\x1c\xb8\x1 9' root@memtest ~# ./sample.py heap.bin === 1c219c00 === b'ERTrustRSAAddTrustCA.crt0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://o csp.usertrust.com\x1d\x7f\xdb\xbd\x9f\x00\x00\x00\x00!\x02\x00\x00\x00\x00\x00 \x000\x82\x02\n\x02\x82\x02\x01\x00\xc2\xf8\xa9?\x1b\x89\xfc<<\x04]=\x906\xb0\ x91:y\xdf\xac\xaf\xe7\xa1\x88k1\xaf\xf0\x8b\xd0\x183\xb8\xdbEj4\xf4\x0 2\x80$(\n\x02\x15\x95^v*\r\x99:\x14[\xf6\xcb\xcbS\xbc\x13M\x01\x887\x94%\x1bB\ xbc"\xd8\x8e\xa3\x96^:\xd92\xdb>\xe8\xf0\x10e\xedt\xe1/\xa7|\xaf\'4\xbb)}\x9b\ xb6\xcf\t\xc8\xe5\xd3\n\xfc\x88eet\n\xdcs\x1c\\\xcd@\xb1\x1c\xd4\xb6\x84\x8cLP \xcfh\x8e\xa8Y\xae\xc2\'N\x82\xa25\xdd\x14\xf4\x1f\xff\xb2w\xd5\x87/\xaan}$\'\ xe7\xc6\xcb&\xe6\xe5\xfeg\x07c\xd8E\r\xdd:Ye9Xz\x92\x99r=\x9c\x84^\x88!\xb8\xd 5\xf4,\xfc\xd9pROx\xb8\xbd<+\x8b\x95\x98\xf5\xb3\xd1h\xcf \x14~L\\_\xe7\x8b\xe 5\xf55\x81\x197\xd7\x11\x08\xb7f\xbe\xd3J\xce\x83W\x00:\xc3\x81\xf8\x17\xcb\x9 26]\xd1\xa3\xd8u\x1b\xe1\x8b\'\xeazHA\xfdE\x19\x06\xad\'\x99N\xc1pG\xdd\xb5\x9 f\x81S\x12\xe5\xb1\x8cH]1C\x17\xe3\x8c\xc6zc\x96K)0N\x84Nb\x19^<\xce\x97\x90\x a5\x7f\x01\xeb\x9d\xe0\xf8\x8b\x89\xdd%\x98=\x92\xb6~\xef\xd9\xf1QQ}-&\xc8iYa\ xe0\xacj\xb8*6\x11\x04zP\xbd2\x84\xbe/\xdcr\xd5\xd7\x1d\x16G\xe4Gf ?\xf4\x96\x c5\xaf\x8e\x01z\xa5\x0fzd\xf5\r\x18\x87\xd9\xae\x88\xd5\xfa\x84\xc1:\xc0i(-\xf 2\rhQ\xaa\xe3\xa5w\xc6\xa4\x90\x0e\xa17\x8b1#G\xc1\t\x08\xebn\xf7x\x9b\xd7\x82 \xfc\x84 \x99I\x19\xb6\x12F\xb1\xfbEU\x16\xa9\xa3e\xac\x9c\x07\x0f\xeak\xdc\x1 f.\x06r\xec\x86\x88\x12\xe4-\xdb_\x05/\xe4\xf0\x03\xd3&3\xe7\x80\xc2\xcdB\xa1\ x174\x0b\x02\x03\x01\x00\x01\x9b\x1f\x90+?%!,.y1\x00\x00\x00\x00\x00\x00\x00\x b8\xd7,\xb2g\x7f\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x04\x 00\x00\x00\xa0\x98\x84#\x94U\x00\x00\x80\x00\x00\x00\x00\x00\x00\x001\x00\x00\ x00\x00\x00\x00\x00\xc0\x87\x84#\x94U\x00\x00p~F6\x94U\x00\x00\xe0\x81F6\x94U\ x00\x00\x00\x00\x00\x00\x00\x00\x00\x00O\xe4HB0c&\xcf1\x00\x00\x00\x00\x00\x00 \x00\x03\x00\x00\x00\x00\x00\x00\x00@\x8aF6\x94U\x00\x00\x00\x00\x00\x00\x04\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\xb1\x17\xb5Y\xe9w\xca!\x00\x00\ x00\x00\x00\x00\x00\x10\xcb,\xb2g\x7f\x00\x00\x80\x8fF6\x94U\x00\x00_lib.c\x00 \x00!\x00\x00\x00\x00\x00\x00\x00\x0c\x01\x00\x00\x03\x00\x00\x00\xe0u\x84#\x9 4U\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00!\x00\x00\x00\x00\x00\x00\x00`\x7fF6 \x94U\x00\x00@\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x11\x0 2\x00\x00\x00\x00\x00\x00\xe3\x02\xa5r%\xb7\xa0\x83\xa1\xa3*\xf8\xc2\x06\xa8\x 98\xcf\xfd\x9d\xc7\x0b\xb1v\x0b\x9d\xa1z\xbd=\xe8\x936[\xaa\xe3\x9c\x04\xc0"\x de\xfaY\xc4\xdb\x8f\x81k!\x07\x9aJ\x9e\xa2\xd0e\xdc\xaaf\xcbj\xfe{T\xb1\x9f10\ x1b\xdf\xa3j#\xa8\x0cy\xe9\x0en\xa6\x1c\x9dlvTyl\xd9\xa5\x07+\xf0{\xf3\xcb\x19 k\xc7\xec\x1c\x17\xf6\xf8\xec]|u\xbb\xfc\x15~\xa6S]\xe6\xfb}2\xb6\xc2o\xd8\x0c qY1c)_\x8cF\xdc\x16\x91\xf6M\xa0tA\xc6\xdf\xc9q\'\xfa#\x8d \x82\xa7\xa4\x8ey\x 82\x98\x8a\xc5$~@M' root@memtest ~# ./sample.py heap.bin === b7ef400 === b'-\x00\x00\x00\x04\x00\x00\x00\x80\xd8\xa3%\x94U\x00\x00\x80\x00\x00\x00\x00\ x00\x00\x00\xc1\x00\x00\x00\x00\x00\x00\x00`\x9f\xa3%\x94U\x00\x00\xe0\x9b\xa3 %\x94U\x00\x00 \x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00m\x8e\ xe1|\x91U\x00\x00`\xd4\xa3%\x94U\x00\x00@\x00\x00\x00\x00\x00\x00\x00 \x00\x00 \x00\x00\x00\x00\x00m\x89\xe1|\x91U\x00\x00ust CA\x00\x00`\x00\x00\x00\x00\x00 \x00\x000\x00\x00\x00\x00\x00\x00\x00\xcd\xbd\xe1|\x91U\x00\x00\x00\xe8\xa3%\x 94U\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9 0\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00\xdd\xc1\xe1|\x91U\x 00\x00`\xd3\xa3%\x94U\x00\x00 \xd4\xa3%\x94U\x00\x00\x00\x00\x00\x00\x00\x00\x 00\x00\x10\x11\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00}\xc1\xe1|\ x91U\x00\x00\x86#http://crl.securetrust.com/SGCA.crl\x00\x00\x00\xa0 \x00\x00\ x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00=\xb9\xf5t\x91U\x00\x00\x00\x00\x0 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00\xd0 \x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00]\xee,k\x91U\ x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x117\x 00\x00\x00\x00\x00\x00p\x03\x1f\x1d\x94U\x00\x00\xf0@6&\x94U\x00\x00\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\xd7\xa3%\x94U\x00\x00\ xe0\xd6\xa3%\x94U\x00\x00 \x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x0 0\x00\xdd\x8c\xe1|\x91U\x00\x00\xf0\xd6\xa3%\x94U\x00\x00`\x00\x00\x00\x00\x00 \x00\x00@\x00\x00\x00\x00\x00\x00\x00\xad\xab\xe1|\x91U\x00\x00\x00\xd8\xa3%\x 94U\x00\x00\xe0\xd8\xa3%\x94U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0 0\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00\xc0j\xa3%\x94U\x00\x00p\x14\xa3%\x94 U\x00\x00 \x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00=\x8d\xe1|\ x91U\x00\x004055Z\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x0 0\x00\x00]\x8c\xe1|\x91U\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`\x00\x00\x00\ x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00]\x8f\xe1|\x91U\x00\x00p\xd5\xa3%\ x94U\x00\x00 \x01\x00\x00\x00\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\x8d\x98 \xe1|\x91U\x00\x00\x06\x0c\x02us1 0\x1e\x06\x03U\x04\n\x0c\x17securetrust corp oration1\x190\x17\x06\x03U\x04\x03\x0c\x10secure global ca\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00q\x03\x00\x00\x00\x00\x00\x00`\x9a\xa3%\x9 4U\x00\x00p\x88\xa3%\x94U\x00\x00oration\x00A\x00\x00\x00\x00\x00\x00\x00\x00\ xd6\xa3%\x94U\x00\x00p\xd7\xa3%\x94U\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00!\ x00\x00\x00\x00\x00\x00\x00\xc0j\xa3%\x94U\x00\x00P\xb7\xa3%\x94U\x00\x00`\x00 \x00\x00\x00\x00\x00\x000\x00\x00\x00\x00\x00\x00\x00M\x8e\xe1|\x91U\x00\x00\x 00\x00\x00\x00\x00\x00\x00\x00\xe0\xda\xa3%\x94U\x00\x00\x90\xd6\xa3%\x94U\x00 \x00J\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x00\x00\x00\x00\xc0j\xa3%\x94U\ x00\x00\x00\xd6\xa3%\x94U\x00\x00 \x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x0 0\x00\x00\x00\xbd\x8f\xe1|\x91U\x00\x00ust CA\x00\x00\xd0\x00\x00\x00\x00\x00\ x00\x00@\x00\x00\x00\x00\x00\x00\x00]\t\xe1|\x91U\x00\x00\x00\x00\x00\x00\x00\ x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0 0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ x00\x00\x00\x00\x81\x00\x00\x00\x00\x00\x00\x00' root@memtest ~# ./sample.py heap.bin === 760f000 === b'\x05\x000A1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x140\x12\x06\x03U\x04\n\x0c\x 0bAffirmTrust1\x1c0\x1a\x06\x03U\x04\x03\x0c\x13AffirmTrust Premium0\x1e\x17\r 100129141036Z\x17\r401231141036Z0A1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x140\x1 2\x06\x03U\x04\n\x0c\x0bAffirmTrust1\x1c0\x1a\x06\x03U\x04\x03\x0c\x13AffirmTr ust Premium0\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02 \x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\xc4\x12\xdf\xa9_\xfeA\xdd\xdd\xf5\x9f\ x8a\xe3\xf6\xac\xe1Q\xa7\xd1"\xc4\n\xa78Hl\xb3\ xf9\xff}\xab\x86W\xe3\xba\xd6\x85xw\xbaC\xeaH\x7f\xf6\xd8\xbe#m\x1e\xbf\xd16lX \\\xf1\xee\xa4\x19T\x1a\xf5\x03\xd2v\xe6\xe1\x8c\xbd<\xb3\xd3HK\xe2\xc8\xf8\x7 f\x92\xa8vF\x9cBe>\xa4\x1e\xc1\x07\x03ZF-\xb8\x97\xf3\xb7\xd5\xb2U!\xef\xba\xd cL\x00\x97\xfb\x14\x95\'3\xbf\xe8CGF\xd2\x08\x99\x16`;\x9a~\xd2\xe6\xed8\xea\x ec\x01\x1e\x10\xe5\x0b\x03\xc9\ x9aB\x00l\xc5\x94~a\xc4\x8a\xdf\x7f\x82\x1a\x0bY\xc4Y2w\xb3\xbc`iV9\xfd\xb4\x0 6{,\xd6d6\xd9\xbdH\xed\x84\x1f~\xa5"\x8f*\xb8B\xf4\x82\xb7\xd4S\x90xN-\x1a\xfd \x81oD\xd7;\x01t\x96B\xe0\x00\xe2.k\xea\xc5\xeer\xac\xbb\xbf\xfe\xea\xaa\xa8\x f8\xdc\xf6\xb2y\x8a\xb6g\x02\x03\x01\x00\x01\xa3B0@0\x1d\x06\x03U\x1d\x0e\x04\ x16\x04\x14\x9d\xc0g\xa6\x0c"\xd9&\xf5E\xab\xa6eR\x11\'\xd8E\xacc0\x0f\x06\x03 U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x0e\x06\x03U\x1d\x0f\x01\x01\x ff\x04\x04\x03\x02\x01\x06\x9b\x1f\x94U\x00\x00\x11\x02\x00\x00\x00\x00\x00\x0 0\xb3WM\x10bN:\xe4\xac\xea\xb8\x1c\xaf2#\xc8\xb3IZQ\x9cv(\x8dy\xaaWF\x17\xd5\x f5R\xf6\xb7D\xe8\x08D\xbf\x18\x84\xd2\x0b\x80\xcd\xc5\x12\xfd\x00U\x05a\x87A\x dc\xb5$\x9e<\xc4\xd8\xc8\xfbp\x9e/x\x96\x83 6\xde|\x0fi\x13\x88\xa5u6\x98\x08\ xa6\xc6\xdf\xac\xce\xe3X\xd6\xb7>\xde\xba\xf3\xeb4@\xd8\xa2\x81\xf5x?/\xd5\xa5 \xfc\xd9\xa2\xd4^\x04\x0e\x17\xad\xfeA\xf0\xe5\xb2r\xfaD\x823B\xe8-X\xf7V\x8cb ?\xbaB\xb0\x9c\x0c\\~.e&\\SO\x00\xb2x~\xa1\r\x99-\x8d\xb8\x1d\x8e\xa2\xc4\xb0\ xfd`\xd00\xa4\x8e\xc8\x04b\xa9\xc4\xed5\xdez\x97\xed\x0e8^\x92/\x93p\xa5\xa9\x 9co\xa7}\x13\x1d~\xc6\x08H\xb1^g\xebQ\x08%\xe9\xe6%kR)\x91\x9c\xd29s\x08W\xde
All these strings appear somewhere in certifi's CA bundle, so I suspected this is somehow related to us calling context.load_verify_locations(certifi.where())
. After spending quite some time on this I found python/cpython#84904, which surprisingly reproduces with pyOpenSSL instead of stdlib:
import os
import gc
import asyncio
from OpenSSL import SSL
import certifi
import psutil
ca_path = certifi.where()
proc = psutil.Process(os.getpid())
async def make_context(sleep: bool) -> None:
ctx = SSL.Context(SSL.TLS_CLIENT_METHOD)
ctx.load_verify_locations(ca_path)
if sleep:
await asyncio.sleep(1)
async def main(n: int, sleep: bool) -> None:
await asyncio.wait([asyncio.create_task(make_context(sleep)) for _ in range(n)])
print("=== without sleep ===")
for _ in range(20):
asyncio.run(main(200, False))
gc.collect()
print(f"{proc.memory_info().rss=:>10}")
print("=== with sleep ===")
for _ in range(20):
asyncio.run(main(200, True))
gc.collect()
print(f"{proc.memory_info().rss=:>10}")
Ubuntu 22.04 Output
# python3.10 repro.py === without sleep === proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 proc.memory_info().rss= 31494144 === with sleep === proc.memory_info().rss= 181592064 # whoops proc.memory_info().rss= 181862400 # but now it stays constant? proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181862400 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952 proc.memory_info().rss= 181501952
Windows 10 Output
# python3.10 repro.py === without sleep === proc.memory_info().rss= 28827648 proc.memory_info().rss= 29130752 proc.memory_info().rss= 29167616 proc.memory_info().rss= 28848128 proc.memory_info().rss= 29155328 proc.memory_info().rss= 29188096 proc.memory_info().rss= 29220864 proc.memory_info().rss= 29184000 proc.memory_info().rss= 29200384 proc.memory_info().rss= 29237248 proc.memory_info().rss= 29270016 proc.memory_info().rss= 29224960 proc.memory_info().rss= 29224960 proc.memory_info().rss= 29261824 proc.memory_info().rss= 29249536 proc.memory_info().rss= 29257728 proc.memory_info().rss= 29298688 proc.memory_info().rss= 29274112 proc.memory_info().rss= 29265920 proc.memory_info().rss= 29229056 === with sleep === proc.memory_info().rss= 30277632 # much better here... proc.memory_info().rss= 31203328 proc.memory_info().rss= 32108544 proc.memory_info().rss= 31698944 proc.memory_info().rss= 31838208 proc.memory_info().rss= 31657984 proc.memory_info().rss= 75530240 # surprise! proc.memory_info().rss= 32440320 # and we're back... proc.memory_info().rss= 77365248 # ... up again! proc.memory_info().rss= 77135872 proc.memory_info().rss= 81215488 proc.memory_info().rss= 79491072 proc.memory_info().rss= 78475264 proc.memory_info().rss= 78393344 proc.memory_info().rss= 32808960 proc.memory_info().rss= 86007808 proc.memory_info().rss= 82329600 proc.memory_info().rss= 83742720 proc.memory_info().rss= 87388160 proc.memory_info().rss= 90755072 proc.memory_info().rss= 89395200 proc.memory_info().rss= 98418688 proc.memory_info().rss= 87658496 proc.memory_info().rss= 95277056 proc.memory_info().rss= 97841152 proc.memory_info().rss= 97341440 proc.memory_info().rss= 96702464 proc.memory_info().rss= 99598336 proc.memory_info().rss= 98127872 proc.memory_info().rss= 100098048 proc.memory_info().rss= 103292928 proc.memory_info().rss= 94601216 proc.memory_info().rss= 95272960 proc.memory_info().rss= 96559104 proc.memory_info().rss= 93630464 proc.memory_info().rss= 99307520 proc.memory_info().rss= 102895616 proc.memory_info().rss= 99205120 proc.memory_info().rss= 106471424 proc.memory_info().rss= 102260736 proc.memory_info().rss= 31555584 # back down! maybe all fine on Windows and just GC after all? proc.memory_info().rss= 99848192 proc.memory_info().rss= 32423936 proc.memory_info().rss= 103653376 proc.memory_info().rss= 31727616 proc.memory_info().rss= 106827776 proc.memory_info().rss= 103124992 proc.memory_info().rss= 100532224 proc.memory_info().rss= 100294656 proc.memory_info().rss= 106164224
So yeah - I'm a bit at a loss what's going on here. Any ideas? I think that the call to load_verify_locations()
may not be the actual suspect, I still see a memory increase when commenting out that line (just much less pronounced). For mitmproxy, it looks like OpenSSL 3 finally has the APIs we need to share contexts more broadly (which should alleviate the problem in our case), but independent of that it looks like something is weirdly wrong here.
Footnotes
-
I've also no idea where
_lib.c
comes from. Maybe I'm missing something, but it looks like there's no such file in either cryptography, cffi, or OpenSSL. ↩
You see this even with latest cryptography right? I ask because there was a memory leak they fixed in openssl 3.0.3, which we shipped in 37.0.2.
Ah yes, sorry - I forgot to specify version information:
Python: 3.10.4
cryptography: 37.0.2 (precompiled wheels)
pyOpenSSL: 22.0.0
Never the easy answer 😅
One more interesting observation using a slightly modified repro:
for ctxs in [10, 20, 30, 40, 50, 60, 70, 80, 90, 80, 70, 60, 50, 40, 30, 20, 10]:
asyncio.run(main(ctxs, True))
gc.collect()
print(f"{ctxs=} {proc.memory_info().rss=:>10}")
ctxs=10 proc.memory_info().rss= 28692480
ctxs=20 proc.memory_info().rss= 36024320
ctxs=30 proc.memory_info().rss= 43335680
ctxs=40 proc.memory_info().rss= 50630656
ctxs=50 proc.memory_info().rss= 57946112
ctxs=60 proc.memory_info().rss= 65241088
ctxs=70 proc.memory_info().rss= 72556544
ctxs=80 proc.memory_info().rss= 79863808
ctxs=90 proc.memory_info().rss= 87158784
ctxs=80 proc.memory_info().rss= 87162880
ctxs=70 proc.memory_info().rss= 87171072
ctxs=60 proc.memory_info().rss= 87171072
ctxs=50 proc.memory_info().rss= 87171072
ctxs=40 proc.memory_info().rss= 87171072
ctxs=30 proc.memory_info().rss= 87171072
ctxs=20 proc.memory_info().rss= 87171072
ctxs=10 proc.memory_info().rss= 87171072
The relationship between the number of allocated contexts and the memory usage is perfectly linear, it just doesn't go down when reducing the number of contexts. For a regular memory leak, we'd expect it to be superlinear due to the increase in contexts per iteration. Seems like OpenSSL is able to reuse memory, but never lets go of it (?).
Edit: More fun stuff: It looks like calling ctypes.CDLL('libc.so.6').malloc_trim(0)
releases back most the memory! I now see a much subtler increase over time...
What's special to asyncio code here? The certificate handling is exactly the same as the sync version, what changes with asyncio is that BIOs are used instead of sockets for the connection.
This should happen even on a sync test.