
ماذا يفعل معظم المبرمجين عندما يكتشفون أن برنامجهم يسرب الذاكرة؟ لا شيء ، دع المستخدم يشتري المزيد من ذاكرة الوصول العشوائي.أجرؤ على افتراض أنهم يأخذون أداة موثوقة تم اختبارها على مدار الوقت مثل valgrind أو libasan ، قم بتشغيل التقرير ومشاهدته. عادة ما تقول أن الكائنات التي تم إنشاؤها على هذا السطر من برنامج كذا وكذا لم يتم تحريرها. و لماذا؟ هذا لم يكتب في أي مكان.
يركز هذا المنشور على أداة البحث عن التسرب المتسرب ، ومفهوم التحليل الإحصائي الأساسي ، وكيف يمكن تطبيقه.
لقد كتبت بالفعل عن topleaked على habrولكني سأكرر الفكرة الرئيسية بشكل عام. إذا لم يتم تحرير بعض الكائنات ، فإنها تتراكم في الذاكرة. هذا يعني أن لدينا العديد من المتواليات المتجانسة والمتشابهة. إذا كان هناك تسرب أكثر مما هو مستخدم بالفعل ، فإن أكثرها شيوعًا هي أجزاء من الأجسام المتسربة. عادةً ما تحتوي برامج C ++ على مؤشرات لفئات vtbl. بهذه الطريقة يمكننا معرفة نوع الأشياء التي ننسى تحريرها. من الواضح أن الجزء العلوي يحتوي على الكثير من القمامة ، والخطوط التي يتم مواجهتها بشكل متكرر ، وسوف يخبرنا Valgrind نفسه بماذا وأين يتدفق بشكل أفضل. ولكن لم يتم إنشاء topleaked في الأصل من أجل التنافس مع التقنيات التي تم تطويرها على مر السنين. لقد تم تصميمه كأداة لحل مشكلة لا يمكن حلها بأي شيء آخر - تحليل التسريبات غير القابلة للتكرار. إذا لم تتمكن من تكرار المشكلة في بيئة اختبار ،إذن أي تحليل ديناميكي لا طائل منه. إذا حدث الخطأ فقط "في المعركة" ، وحتى غير مستقر ، فإن الحد الأقصى الذي يمكننا الحصول عليه هو السجلات وتفريغ الذاكرة. يمكن تحليل هذا التفريغ في قصوى.
C++ , - abort()
#include <iostream>
#include <assert.h>
#include <unistd.h>
class A {
size_t val = 12345678910;
virtual ~A(){}
};
int main() {
for (size_t i =0; i < 1000000; i++) {
new A();
}
std::cout << getpid() << std::endl;
abort();
}
topleaked
./toleaked leak.core
— .
0x0000000000000000 : 1050347
0x0000000000000021 : 1000003
0x00000002dfdc1c3e : 1000000
0x0000558087922d90 : 1000000
0x0000000000000002 : 198
0x0000000000000001 : 180
0x00007f4247c6a000 : 164
0x0000000000000008 : 160
0x00007f4247c5c438 : 153
0xffffffffffffffff : 141
, 0x2dfdc1c3e, 12345678910, . , . , , gdb gdb . -ogdb — , gdb.
$ ./topleaked -n10 -ogdb /home/core/leak.1002.core | gdb leak /home/core/leak.1002.core
...< gdb >
#0 0x00007f424784e6f4 in __GI___nanosleep (requested_time=requested_time@entry=0x7ffcfffedb50, remaining=remaining@entry=0x7ffcfffedb50) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28 ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
(gdb) $1 = 1050347
(gdb) 0x0: Cannot access memory at address 0x0
(gdb) No symbol matches 0x0000000000000000.
(gdb) $2 = 1000003
(gdb) 0x21: Cannot access memory at address 0x21
(gdb) No symbol matches 0x0000000000000021.
(gdb) $3 = 1000000
(gdb) 0x2dfdc1c3e: Cannot access memory at address 0x2dfdc1c3e
(gdb) No symbol matches 0x00000002dfdc1c3e.
(gdb) $4 = 1000000
(gdb) 0x558087922d90 <_ZTV1A+16>: 0x87721bfa
(gdb) vtable for A + 16 in section .data.rel.ro of /home/g.smorkalov/dlang/topleaked/leak
(gdb) $5 = 198
(gdb) 0x2: Cannot access memory at address 0x2
(gdb) No symbol matches 0x0000000000000002.
(gdb) $6 = 180
(gdb) 0x1: Cannot access memory at address 0x1
(gdb) No symbol matches 0x0000000000000001.
(gdb) $7 = 164
(gdb) 0x7f4247c6a000: 0x47ae6000
(gdb) No symbol matches 0x00007f4247c6a000.
(gdb) $8 = 160
(gdb) 0x8: Cannot access memory at address 0x8
(gdb) No symbol matches 0x0000000000000008.
(gdb) $9 = 153
(gdb) 0x7f4247c5c438 <_ZTVN10__cxxabiv120__si_class_type_infoE+16>: 0x47b79660
(gdb) vtable for __cxxabiv1::__si_class_type_info + 16 in section .data.rel.ro of /usr/lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) $10 = 141
(gdb) 0xffffffffffffffff: Cannot access memory at address 0xffffffffffffffff
(gdb) No symbol matches 0xffffffffffffffff.
(gdb) quit
, . $4 = 1000000 . x info symbol . , vtable for A, A.
. , , 15. , .
, ?
— ? , , . topleaked . , , , . ? , , . , , .
…
. , . — . 3 . , . 3 , - . , 2-3 — . . , — , . C++ . , . C, D, Rust, Go NodeJS. , js .
. , , , , close. , . ( ), , fd (512000 ) . . . , , .
topleaked — . , , . , , . : . state — enum, . : , , websocket, . , , .
. Topleaked , , 8 8- . - , , , - . - , . , vtbl, . , , “ ”. vtbl - state. , . .
C++ — ABI - . POD trivial C. , , . . , linux gcc , vtbl — . offsetof(state) . :
struct Base {
virtual void foo() = 0;
};
struct Der : Base {
size_t a = 15;
void foo() override {
}
};
int main()
{
for (size_t i = 0; i < 10000; ++i) {
new Der;
}
auto d = new Der;
cout << offsetof(Der, a) << endl;
abort();
return 0;
}
offsetof Der::a, “” 10000 . topleaked
topleaked my_core.core
0x0000000000000000 : 50124
0x000000000000000f : 10005
0x0000000000000021 : 10004
0x000055697c45cd78 : 10002
0x0000000000000002 : 195
0x0000000000000001 : 182
0x00007fe9cbd6c000 : 167
0x0000000000000008 : 161
0x00007fe9cbd5e438 : 154
0x0000000000001000 : 112
0x000055697c45cd78 vtbl Der. offsetof 8. , 8 . topleaked — . -f , , --memberOffset — -f, --memberType — . uint8, uint16, uint32 uint64.
topleaked my_core.core -f0x55697c45cd78 --memberOffset=8 --memberType=uint64
:
0x000000000000000f : 10001
0x000055697ccaa080 : 1
10000 0x0f, , .
Happy End
. , , , . , . , . , , . , TCP, — websocket upgrade, - . . — , . , (, ) TCP. , , . . , , — TCP Keep Alive. https://blog.cloudflare.com/when-tcp-sockets-refuse-to-die/
, . , . websocket . , .
D
readFile(name, offset, limit)
.findMember!uint64_t(pattern, memberOffset)
.findMostFrequent(size).printResult(format);
findMember — , , findMostFrequent — , . (ranges) . , , , .
P.S. Crazy Panda , . , topleaked.