أهلا،
أولاً ، شكرًا لـ libseccomp - لقد استخدمناه بسعادة في الإنتاج لعدة سنوات حتى الآن ، ولم نواجه أي مشكلات (حتى الآن). لست متأكدًا مما إذا كان هذا خطأ في الكود الخاص بنا ، أو سوء فهم للوثائق ، أو أي شيء آخر - لكنني أمضيت الشهر الماضي في محاولة تعقب ذلك دون جدوى.
لقد قمنا مؤخرًا بترقية الحزم في حاويات Docker الخاصة بنا ، والتي تضمنت ترقية من libseccomp 2.3.3 (إصدار في مستودعات دبيان المستقرة) إلى 2.4.3. كانت هناك حزم نظام أخرى تمت ترقيتها أيضًا ، لكنني لم أسجلها. لم تتم ترقية النواة الخاصة بنا ، وهي الإصدار 4.19.0-8-amd64.
نستخدم SCMP_ACT_TRACE
، وننشئ عامل تصفية يتكون فقط من قواعد SCMP_ACT_ALLOW
التي تمت إضافتها باستخدام أرقام syscall الأصلية ، بدلاً من أرقام libseccomp الزائفة. قمنا بتفكيك عملية مساعدة 64 بت تقوم ببناء وتحميل عامل تصفية seccomp قبل exec
-ing ثنائي آخر 64 بت.
كمرجع ، هذا هو مجمل روتين التهيئة seccomp الخاص بنا ، باستخدام تدقيق الأخطاء المماثل لصفحة الرجل seccomp_rule_add
.
ومع ذلك ، فإن مكالمتنا إلى seccomp_load
بدأت في إرجاع -EINVAL
، في حدود 1 / 100،000 عملية تهيئة. (عدم القدرة على إعادة إنتاجه بشكل موثوق جعل هذا الأمر شاقًا للتصحيح). لم تكن هناك تغييرات في التعليمات البرمجية لتطبيقنا خلال هذا الوقت. تتطابق طلبات السحاب التي تمت إضافتها إلى عامل التصفية في جميع عمليات التشغيل.
أي أفكار حول الخطأ الذي يمكن أن يحدث (أو حتى كيفية البحث بشكل أكبر في الخطأ الذي يحدث) ، أو إذا كان هذا متوقعًا بطريقة ما؟ لا يوجد الكثير من الأجزاء المتحركة الديناميكية ، ولم أتمكن من العثور على أي شيء في الوثائق حول سبب حدوث ذلك.
مرحبًا Xyene ،
لا يوجد الكثير من الأماكن التي ترجع -EINVAL في مسار الكود seccomp_load (). بناءً على فحص سريع لرمز libseccomp v2.4.3 ، يبدو أنه إما بسبب scmp_filter_ctx
غير صالح أو أن النواة تشكو من استدعاء prctl(...)
الذي يقوم بتحميل عامل التصفية.
بالنظر إلى أن الإصدار 2.4.3 يعمل بشكل عام ، ولم تقم بتغيير النواة الخاصة بك ، يبدو من المشكوك فيه أن استدعاء prctl(...)
هو السبب الذي يقودنا إلى سياق مرشح غير صالح. هل لاحظت أي سلوك غريب آخر في برنامجك منذ الترقية؟ أتساءل عما إذا كانت هناك مشكلة تلف الذاكرة في مكان آخر تسبب المشكلة.
في حين أنه من المحتمل دائمًا أن يكون الخطأ في libseccomp ، فإننا نقوم بتشغيل كل إصدار من خلال سلسلة من عمليات الفحص التي تشمل عمليات تشغيل valgrind لجميع اختبارات الانحدار بالإضافة إلى التحليل الثابت باستخدام كل من clang و Coverity.
أيضًا ، على الرغم من أن هذا لا يساعد في الإصدار 2.4.3 ، فإن أحد التحسينات التي نستهدفها لإصدار v2.5.0 الجاهز تقريبًا هو التوثيق المحسن والتعامل مع رموز الأخطاء.
لقد قمنا مؤخرًا بترقية الحزم في حاويات Docker الخاصة بنا ، والتي تضمنت ترقية من libseccomp 2.3.3 (إصدار في مستودعات دبيان المستقرة) إلى 2.4.3. كانت هناك حزم نظام أخرى تمت ترقيتها أيضًا ، لكنني لم أسجلها. لم تتم ترقية النواة الخاصة بنا ، وهي الإصدار 4.19.0-8-amd64.
نشكرك على التحقق من أن التعليمات البرمجية الخاصة بك والنواة الأساسية لم تتغير. يجب أن يساعد ذلك في تعقب المشكلة.
كمرجع ، هذا هو مجمل روتين التهيئة seccomp الخاص بنا ، باستخدام تدقيق الأخطاء المماثل لصفحة الرجل
seccomp_rule_add
.
مرشح الخاص بك يبدو معقولاً بالنسبة لي.
أي أفكار حول الخطأ الذي يمكن أن يحدث (أو حتى كيفية البحث بشكل أكبر في الخطأ الذي يحدث) ، أو إذا كان هذا متوقعًا بطريقة ما؟ لا يوجد الكثير من الأجزاء المتحركة الديناميكية ، ولم أتمكن من العثور على أي شيء في الوثائق حول سبب حدوث ذلك.
لقد بحثت في كود v2.4.3 seccomp_load()
، وأعتقد أن هناك مكانين فقط يُنشئ فيه libseccomp رمز إرجاع -EINVAL
:
seccomp_load()
في السطر 283_gen_bpf_build_bpf()
على السطر 1657كلا الخطأين المذكورين أعلاه ناتج عن عامل تصفية غير صالح. يبدو أن هذا غير مرجح بالنسبة لي بناءً على كود التصفية الخاص بك.
تجدر الإشارة إلى أن قيمة الإرجاع الافتراضية لـ kernel في seccomp_set_mode_filter()
هي -EINVAL
، لذلك من المحتمل أن شيئًا آخر على النظام قد تغير ، مما يؤدي بنا إلى السقوط في هذا المسار. ذكرت أنك تعمل في Docker ؛ هل تقوم بتعطيل عامل تصفية Docker seccomp الافتراضي؟
سأميل إلى إضافة المزيد من التصحيح إلى التعليمات البرمجية الخاصة بك داخل إذا بعد فشل seccomp_load()
. على سبيل المثال ، يمكننا إخراج PFC و / أو BPF للمرشح نفسه للتحقق من أنه يبدو معقولًا. انظر seccomp_export_pfc()
و seccomp_export_bpf()
.
لقد بحثت في كود v2.4.3
seccomp_load()
، وأعتقد أن هناك مكانين فقط يُنشئ فيه libseccomp رمز إرجاع-EINVAL
:
seccomp_load()
في السطر 283_gen_bpf_build_bpf()
على السطر 1657
ضع في اعتبارك أن أي إخفاقات تم العثور عليها في gen_bpf_generate(...)
، أو أقل ، يتم دمجها بشكل فعال في -ENOMEM بواسطة sys_filter_load(...)
على src / system.c: 267 .
أكره العودة إلى "فساد الذاكرة!" بسرعة كبيرة ، ولكن يبدو أن هذا هو الحال هنا.
شكرا على الردود السريعة والمفصلة! لقد أوجدوا عدة طرق للاستكشاف:
هل لاحظت أي سلوك غريب آخر في برنامجك منذ الترقية؟ أتساءل عما إذا كانت هناك مشكلة تلف الذاكرة في مكان آخر تسبب المشكلة.
لا ، هذا فقط. تستمر اختبارات الوحدة والتكامل الخاصة بنا في النجاح ، وبغض النظر عن هذا EINVAL
النادر جدًا ، لم يتم تسجيل أي أخطاء في المنتج. هذا بالتأكيد يجعل الأمر محيرا. لقد اشتبهت أيضًا في وجود تلف في الذاكرة ، لكنني لم أتمكن من العثور على أي دليل يدعم ذلك:
لمزيد من السياق:
seccomp_init
إلخ.أثناء كتابة هذا ، خطرت لي فكرة: لقد سمعت قصص رعب حول malloc
كونها غير آمنة للاستخدام بعد التفرع ، ولدينا بعضها داخل libseccomp نفسها. تطبيق Python نفسه _ هو _ متعدد الخيوط ، لكننا نحمل دائمًا GIL أثناء وجوده في الكود الأصلي ، لذا يجب أن يكون هذا آمنًا (؟). لقد سمعت فقط عن الجمود الذي يحدث من خلال malloc-after-fork ، رغم ذلك. (أعتقد أن هذا يجعل الأمر التالي من العمل يتحرك seccomp_init
وآخرون. قبل مفترق الطرق ، فقط استدعاء seccomp_load
post-fork ، ومعرفة ما إذا كانت الأخطاء تستمر في الحدوث.)
سأميل إلى إضافة المزيد من تصحيح الأخطاء إلى التعليمات البرمجية الخاصة بك داخل if بعد فشل seccomp_load ().
شكرا على اقتراحك! لقد قمت بإضافة مكالمة إلى seccomp_export_pfc
، وكذلك تفريغ محتويات المدخلات إلى المرشح ( config->syscall_whitelist
). سأتابع في المرة القادمة التي يفشل فيها هذا.
مرحبًا Xyene - منذ مرور حوالي أسبوع ، أردت فقط التحقق ومعرفة ما إذا كان هناك أي شيء جديد وجدته؟
ليس بعد ، للأسف. بعد إضافة التصحيح إلى seccomp_export_pfc
، كان الوضع صامتًا. بالأمس قمت بدفع هذا التصحيح إلى جميع الأجهزة الافتراضية الخاصة بنا (بدلاً من مجرد اختبار واحد) على أمل التقاط المشكلة عند حدوثها في النهاية.
أجد الصمت غريبًا ، لكن في الوقت الحالي أعتمد عليه بالمصادفة نظرًا لأن منطق التصحيح / التصدير يحدث _ بعد_ فشل seccomp_load
، لذلك لا ينبغي أن يؤثر على الفشل نفسه.
تقدم!
تبين أن سبب صمت الصوت هو أن seccomp_export_bpf
كان segfaulting (هل يجب أن يتم ذلك ، إذا تم استدعاؤه بعد seccomp_load
؟) ، وقد تم الإبلاغ عن ذلك في مكان آخر وليس المكان الذي كنت أبحث فيه عن حالات فشل seccomp. الأهم من ذلك ، لقد واجهت حالة يمكنني فيها إعادة إنتاج المشكلة بشكل موثوق به في ~ 150 طلبًا ، لذلك مع بعض أعمال السباكة ، يجب أن أكون قادرًا على استخراج بعض مقالب النفايات الأساسية.
حسنًا ، لقد سحبت تفريغًا ، وكان هذا هو التتبع: https://gist.github.com/Xyene/920f1cb098784a031f53c66a2f49d167
كان هذا مشكوكًا فيه بعض الشيء ، لأنه تحطم داخل روتين jemalloc realloc
. علاوة على ذلك ، فإن استخدام glibc malloc بدلاً من ذلك يحل المشكلة (لسوء الحظ ، ليس خيارًا طويل الأجل في هذه الحالة بسبب مشكلات التجزئة).
بعد ذلك ، قمت بسحب jemalloc ، وقمت بتجميعه باستخدام -O0
ورموز تصحيح الأخطاء ، وأعدت إعادة الإنتاج. هذه المرة تحطمت في seccomp_load
، وليس بعد ذلك! لقد قمت بتحميل هذا التتبع هنا: https://gist.github.com/Xyene/5da56168bcea337da85b2cd30704d12e
مقتطف من هذا التتبع:
#9 0x00007ff962698495 in free (ptr=0x5a5a5a5a5a5a5a5a) at src/jemalloc.c:2867
No locals.
#10 0x00007ff96062d087 in _program_free (prg=prg@entry=0x7ff95e963010) at gen_bpf.c:511
No locals.
#11 0x00007ff96062f605 in gen_bpf_release (program=program@entry=0x7ff95e963010) at gen_bpf.c:1986
No locals.
#12 0x00007ff96062c04f in sys_filter_load (col=col@entry=0x7ff95e9a5000) at system.c:293
rc = -1
prgm = 0x7ff95e963010
#13 0x00007ff96062b666 in seccomp_load (ctx=ctx@entry=0x7ff95e9a5000) at api.c:286
col = 0x7ff95e9a5000
عند البحث في jemalloc ، يبدو أن 0x5a
يُستخدم لوضع علامة على البايتات المجانية على أنها مجانية ، مع الهدف المحدد لتعطل الكود الذي يحاول تحرير شيء تم تحريره بالفعل.
gen_bpf.c:511
في الإصدار 2.4.3 هو: https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L505 -L513
لكن هذا ليس منطقيًا نظرًا لأن عمر البرنامج عبارة عن نص فقط sys_filter_load
:
أعتقد أنني اكتشفت مشكلة واحدة على الأقل. في gen_bpf_generate
؛
state.bpf = prgm
طالما أن zmalloc
لم يفشل. بعد ذلك ، تم استدعاء _gen_bpf_build_bpf
، واستنادًا إلى rc
، تم تعيين state.bpf
على NULL
.
بالنظر إلى الحالة التي يكون فيها rc != 0
، state.bpf
لا يزال مضبوطًا على prgm
في وقت المكالمة إلى _state_release
. سيؤدي هذا إلى تحرير الذاكرة المشار إليها بـ prgm
.
بعد ذلك ، gen_bpf_generate
سوف return prgm
، والذي على الرغم من تحريره ، لا يزال مؤشرًا غير صفري.
مرة أخرى في sys_filter_load
، gen_bpf_generate
عائدات ، و prgm
ليس - NULL
لذلك يستمر.
أخيرًا ، في نهاية sys_filter_load
، يتم استدعاء gen_bpf_release
على prgm
المجاني بالفعل.
هذا لا يعالج القلق بشأن سبب فشل _gen_bpf_build_bpf
في المقام الأول ، لكنه يبدو شيئًا سيئًا يمكن أن يحدث إذا حدث.
تحرير: في الواقع ، يبدو أنه تم إصلاحه على الأرجح كأثر جانبي لـ https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace.
بالنظر إلى الحالة التي يكون فيها rc! = 0 ، لا يزال يتم تعيين state.bpf على prgm في وقت استدعاء _state_release. سيؤدي هذا إلى تحرير الذاكرة المشار إليها بواسطة prgm.
آه ها! التقاط جيد Xyene!
أعتقد أننا بحاجة إلى إصلاح هذا أبعد من 3a1d1c977065f204b96293cccfe7d3e5aa0d7ace ، دعني أفكر في هذا لمدة دقيقة ... لا أعتقد أن الإصلاح سيكون صعبًا للغاية ... ومعرفة ما إذا كان بإمكاني التوصل إلى علاقات عامة.
أعتقد أننا بحاجة إلى إصلاح هذا بعد 3a1d1c9 ، دعني أفكر في هذا لمدة دقيقة ... لا أعتقد أن الإصلاح سيكون صعبًا للغاية ... ومعرفة ما إذا كان بإمكاني التوصل إلى علاقات عامة.
عفوًا ، كنت أبحث في الكود القديم عندما كتبت ذلك ؛ نعم ، أعتقد أن 3a1d1c9 يقوم بإصلاح هذا الأمر بالنسبة لنا ، لكننا سنحتاج إلى تصحيح لفرع الإصدار 2.4. سأعمل على ذلك الآن.
_ (ميتا: سأستمر في تحديث هذه الرسالة بالنتائج التي توصلت إليها مع تقدمي في العمل ، لذلك لدي مكان لتدوينها دون إرسال بريد إلكتروني غير مرغوب فيه إليكم يا رفاق:) _
حسنًا ، مرة أخرى في 2.4.3 مع التصحيح المطبق ، تمكنت من سحب الفلتر الذي فشل: link .
السبب الذي تم الإبلاغ عنه الآن هو ENOMEM
بدلاً من EINVAL
، والذي أعتقد أنه متوقع نظرًا لفشل _gen_bpf_build_bpf
وإرجاع برنامج NULL
. ومع ذلك ، فإن PFC تطبع بشكل جيد. تعديل كود seccomp للإبلاغ عن قيمة الإرجاع _gen_bpf_build_bpf
يظهر EFAULT
كسبب.
كاختراق سريع ، قمت بتشغيل :%s/return -EFAULT/abort()
على src/gen_bpf.c
، وتمكنت من استخراج تتبع المكدس هذا:
EFAULT Stacktrace
(gdb) bt full
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
set = {__val = {0, 140084028365964, 140083248439464, 140083248438968, 140083248431088, 140084028368143, 28659884033, 140083965300736,
140083248439464, 140083248438968, 140083248431088, 140084028351031, 140084019988760, 140083248439624, 140083248431200, 140084028372597}}
pid = <optimized out>
tid = <optimized out>
ret = <optimized out>
#1 0x00007f67daa4d55b in __GI_abort () at abort.c:79
save_stage = 1
act = {__sigaction_handler = {sa_handler = 0x7f67d6f3eec0, sa_sigaction = 0x7f67d6f3eec0}, sa_mask = {__val = {140083965300736,
140083965300736, 0, 0, 140083248438968, 140083248438968, 140083248439464, 140083248431504, 140084028417173, 140083964793344,
140083965300736, 140083248431552, 140083994791895, 140083248431552, 140083994787642, 140083965300736}}, sa_flags = -1404894496,
sa_restorer = 0x0}
sigs = {__val = {32, 0 <repeats 15 times>}}
#2 0x00007f67d8bfd455 in _gen_bpf_build_bpf (state=0x7f67ac4302e0, col=0x7f67d6f63040) at gen_bpf.c:1943
rc = 0
iter = 1
h_val = 1425818561
res_cnt = 0
jmp_len = 0
arch_x86_64 = 0
arch_x32 = -1
instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4,
hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
i_iter = 0x7f67d6fdcb60
b_badarch = 0x7f67d6fd9000
b_default = 0x7f67d6fd9060
b_head = 0x7f67d6fda1a0
b_tail = 0x7f67d6fd9000
b_iter = 0x0
b_new = 0x7f67d6fe3300
b_jmp = 0x0
db_secondary = 0x0
pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0,
syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#3 0x00007f67d8bfd560 in gen_bpf_generate (col=0x7f67d6f63040) at gen_bpf.c:1971
rc = 0
state = {htbl = {0x0 <repeats 256 times>}, attr = 0x7f67d6f63044, bad_arch_hsh = 889798935, def_hsh = 742199527, arch = 0x7f67ac4301e0,
bpf = 0x7f67d6f64010}
prgm = 0x7f67d6f64010
#4 0x00007f67d8bf64a7 in sys_filter_load (col=0x7f67d6f63040) at system.c:265
rc = 32615
prgm = 0x0
#5 0x00007f67d8bf4f10 in seccomp_load (ctx=0x7f67d6f63040) at api.c:287
col = 0x7f67d6f63040
يتوافق مع السطر 1943:
نظرًا لطبيعة الاستبدال ، أعتقد أنه يمكننا استبعاد أي EFAULT
في أي وظيفة مساعدة ، نظرًا لأن تلك الوظائف كانت ستجهض أولاً.
بعد ذلك ، حاولت إعادة إنتاج نفس الشيء باستخدام HEAD - ما زال يحدث. بعد ذلك ، %s:/goto build_bpf_free_blks/abort()
وكرر الأمر. كان السبب:
لحسن الحظ ، كانت هذه الوظيفة قصيرة ، ولم يكن بها سوى عدد قليل من نقاط الفشل. جولة أخرى من عمليات الإدراج في وقت لاحق بـ abort
؛
أثر
(gdb) bt full
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
set = {__val = {0, 140050183343588, 0, 448, 140049402494880, 140049402509040, 140049402494832, 140050183342988, 140049402495088,
140049402509040, 140049402494896, 140050183343588, 4294967296, 140049402509040, 140049402509040, 140049402509040}}
pid = <optimized out>
tid = <optimized out>
ret = <optimized out>
#1 0x00007f5ff953055b in __GI_abort () at abort.c:79
save_stage = 1
act = {__sigaction_handler = {sa_handler = 0x7f5ff595d260, sa_sigaction = 0x7f5ff595d260}, sa_mask = {__val = {139642271694862,
140050119389792, 0, 0, 140049402502840, 0, 140049402503336, 140049402502888, 140049402502840, 112, 384, 140049402502840, 140050149861504,
140049402495328, 140050149857273, 392}}, sa_flags = 448, sa_restorer = 0x7f5ff595d240}
sigs = {__val = {32, 0 <repeats 15 times>}}
#2 0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
rc = -12
i_new = 0x0
i_iter = 0x7f5ff59fa178
old_cnt = 48
iter = 1
#3 0x00007f5ff76f3716 in _gen_bpf_build_bpf (state=0x7f5fcae302d0, col=0x7f5ff59c5000) at gen_bpf.c:2223
rc = 0
iter = 1
h_val = 1425818561
res_cnt = 0
jmp_len = 0
arch_x86_64 = 0
arch_x32 = -1
instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4,
hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
i_iter = 0x7f5ff59e1b60
b_badarch = 0x7f5ff59de000
b_default = 0x7f5ff59de060
b_head = 0x7f5ff59df1a0
b_tail = 0x7f5ff59de000
b_iter = 0x7f5ff59df1a0
b_new = 0x7f5ff59e8300
b_jmp = 0x7f5ff59df0e0
db_secondary = 0x0
pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0,
syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#4 0x00007f5ff76f3874 in gen_bpf_generate (col=0x7f5ff59c5000, prgm_ptr=0x7f5fcae30b40) at gen_bpf.c:2270
rc = 0
state = {htbl = {0x0, 0x7f5ff593ef80, 0x7f5ff593efe0, 0x7f5ff593efc0, 0x0, 0x7f5ff595d000, 0x7f5ff593ef60, 0x7f5ff593ef00,
0x0 <repeats 248 times>}, attr = 0x7f5ff59c5004, bad_arch_hsh = 889798935, def_hsh = 742199527, bpf = 0x7f5ff5964010,
arch = 0x7f5fcae301c0, b_head = 0x7f5ff59e8300, b_tail = 0x7f5ff59de120, b_new = 0x7f5ff59e8300}
prgm = <optimized out>
#5 0x00007f5ff76eb275 in sys_filter_load (col=0x7f5ff59c5000, rawrc=false) at system.c:307
rc = 0
prgm = 0x0
#6 0x00007f5ff76e9505 in seccomp_load (ctx=0x7f5ff59c5000) at api.c:386
col = 0x7f5ff59c5000
rawrc = false
لذا فقد فشل realloc
مرة أخرى ، وعاد _bpf_append_blk
-ENOMEM
الذي تم إخفاءه بواسطة _gen_bpf_build_bpf
وتحول إلى -EFAULT
. هذه ليست مشكلة كبيرة ، ولكن نظرًا لأنك قلت إن الإبلاغ الأفضل عن الأخطاء هو هدف 2.5 ، فقد أذكر ذلك لأن هذا يبدو في النطاق:
بعض الوخز مع GDB:
(gdb) f 2
#2 0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
452 abort();
(gdb) info args
prg = 0x7f5ff5964010
blk = 0x7f5ff59df1a0
(gdb) print prg->blks
$4 = (bpf_instr_raw *) 0x7f5ff59fa000
(gdb) x/32bx &prg->blks
0x7f5ff5964018: 0x00 0xa0 0x9f 0xf5 0x5f 0x7f 0x00 0x00
0x7f5ff5964020: 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a
0x7f5ff5964028: 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a 0x5a
0x7f5ff5964030: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
(gdb) print ((prg)->blk_cnt * sizeof(*((prg)->blks)))
$5 = 392
(gdb) print prg->blk_cnt
$6 = 49
هذا بالفعل بدأ في الظهور وكأنه فشل في تخصيص ...
آها ، لقد وصلت هذه القصة أخيرًا إلى نهايتها _المخيفة _ لقد اكتشفت ما كان يحدث ، وتحققت من الإصلاح: وجه مبتسم قليلاً:
نظرًا لأنها قد تكون قصة مثيرة للاهتمام ، فإليك ما يلي:
عادةً ما تكون العملية الرئيسية التي تنفصل عن العامل في خدمة RSS تبلغ 80 ميجا بايت تقريبًا. بعد الانقسام ، فإنه يقيد استخدام الذاكرة عبر rlimit
، أحيانًا إلى 64 ميجا بايت. هذا يضعه في موقف حيث يتجاوز استخدامه الحالي للذاكرة الحد المسموح به ، ولكن هذا مسموح به بواسطة rlimit
. _ في معظم الأوقات ، سيكون لمخصص الذاكرة ذاكرة خالية كافية لخدمة إجراءات تهيئة libseccomp دون طلب المزيد من النواة. ولكن عندما لا تفعل ذلك ، وتحتاج إلى طلب مساحة لساحة إضافية أو شيء ما ، فلن توفرها النواة لأن العملية تجاوزت بالفعل حدودها.
في 2.4.3 ، ظهر هذا الفشل في الحصول على الذاكرة في EINVAL
وخالٍ مزدوج. في المنشور الرئيسي- https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace ، تم الإبلاغ عن EFAULT
بدلاً من ذلك. مع تطبيق https://github.com/seccomp/libseccomp/pull/257 ، تم الإبلاغ عن ENOMEM
بشكل صحيح.
يصبح سبب حدوث ذلك نادرًا جدًا بعد ذلك: يعتمد كليًا على ما إذا كان المخصص لديه ذاكرة كافية لبناء برنامج BPF دون طلب المزيد من النواة. يُعد مُخصص glibc أكثر مرونة فيما يتعلق بالسماح بتراكم التجزئة ، لذلك لم يحدث هذا أبدًا في مكانه. يضع jemalloc حدودًا أكثر إحكامًا ، ويؤدي إلى زيادة احتمالية الحاجة إلى طلب ذاكرة خلال seccomp_load
- يكفي فقط لملاحظة الإخفاقات الناتجة ، ولكن لا يزال من الغضب تعقبها.
الإصلاح ، إذن ، هو ببساطة نقل جميع المكالمات setrlimit
إلى _after_ seccomp_load
. عند القيام بذلك ، لم يعد يخفق realloc
في _bpf_append_blk
، ويتم تحميل الفلتر بنجاح. هذا يعني أن الفلتر يحتاج إلى السماح بـ setrlimit
، لكن في حالتي كان هذا مقبولًا. بشكل عام ، أعتقد أنه سيتم حل هذه المشكلة بشيء مثل https://github.com/seccomp/libseccomp/issues/123.
pcmoore ، drakenclimber - شكرًا مرة أخرى على كل ما
تم إصلاح هذا الخطأ عن طريق الالتزام https://github.com/seccomp/libseccomp/commit/c0a6e6fd15f74c429a0b74e0dfd4de5a29aabebd
التعليق الأكثر فائدة
ليس بعد ، للأسف. بعد إضافة التصحيح إلى
seccomp_export_pfc
، كان الوضع صامتًا. بالأمس قمت بدفع هذا التصحيح إلى جميع الأجهزة الافتراضية الخاصة بنا (بدلاً من مجرد اختبار واحد) على أمل التقاط المشكلة عند حدوثها في النهاية.أجد الصمت غريبًا ، لكن في الوقت الحالي أعتمد عليه بالمصادفة نظرًا لأن منطق التصحيح / التصدير يحدث _ بعد_ فشل
seccomp_load
، لذلك لا ينبغي أن يؤثر على الفشل نفسه.