Libseccomp: BUG: لا يعالج gen_bpf_generate () الفشل بشكل صحيح

تم إنشاؤها على ٢٨ مايو ٢٠٢٠  ·  15تعليقات  ·  مصدر: seccomp/libseccomp

أهلا،

أولاً ، شكرًا لـ 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 عملية تهيئة. (عدم القدرة على إعادة إنتاجه بشكل موثوق جعل هذا الأمر شاقًا للتصحيح). لم تكن هناك تغييرات في التعليمات البرمجية لتطبيقنا خلال هذا الوقت. تتطابق طلبات السحاب التي تمت إضافتها إلى عامل التصفية في جميع عمليات التشغيل.

أي أفكار حول الخطأ الذي يمكن أن يحدث (أو حتى كيفية البحث بشكل أكبر في الخطأ الذي يحدث) ، أو إذا كان هذا متوقعًا بطريقة ما؟ لا يوجد الكثير من الأجزاء المتحركة الديناميكية ، ولم أتمكن من العثور على أي شيء في الوثائق حول سبب حدوث ذلك.

bug prioritmedium

التعليق الأكثر فائدة

ليس بعد ، للأسف. بعد إضافة التصحيح إلى seccomp_export_pfc ، كان الوضع صامتًا. بالأمس قمت بدفع هذا التصحيح إلى جميع الأجهزة الافتراضية الخاصة بنا (بدلاً من مجرد اختبار واحد) على أمل التقاط المشكلة عند حدوثها في النهاية.

أجد الصمت غريبًا ، لكن في الوقت الحالي أعتمد عليه بالمصادفة نظرًا لأن منطق التصحيح / التصدير يحدث _ بعد_ فشل seccomp_load ، لذلك لا ينبغي أن يؤثر على الفشل نفسه.

ال 15 كومينتر

مرحبًا 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 :

كلا الخطأين المذكورين أعلاه ناتج عن عامل تصفية غير صالح. يبدو أن هذا غير مرجح بالنسبة لي بناءً على كود التصفية الخاص بك.

تجدر الإشارة إلى أن قيمة الإرجاع الافتراضية لـ 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 :

ضع في اعتبارك أن أي إخفاقات تم العثور عليها في gen_bpf_generate(...) ، أو أقل ، يتم دمجها بشكل فعال في -ENOMEM بواسطة sys_filter_load(...) على src / system.c: 267 .

أكره العودة إلى "فساد الذاكرة!" بسرعة كبيرة ، ولكن يبدو أن هذا هو الحال هنا.

شكرا على الردود السريعة والمفصلة! لقد أوجدوا عدة طرق للاستكشاف:

هل لاحظت أي سلوك غريب آخر في برنامجك منذ الترقية؟ أتساءل عما إذا كانت هناك مشكلة تلف الذاكرة في مكان آخر تسبب المشكلة.

لا ، هذا فقط. تستمر اختبارات الوحدة والتكامل الخاصة بنا في النجاح ، وبغض النظر عن هذا EINVAL النادر جدًا ، لم يتم تسجيل أي أخطاء في المنتج. هذا بالتأكيد يجعل الأمر محيرا. لقد اشتبهت أيضًا في وجود تلف في الذاكرة ، لكنني لم أتمكن من العثور على أي دليل يدعم ذلك:

لمزيد من السياق:

  • البرنامج عبارة عن تطبيق Python يستدعي بعض C ++ عبر Cython (يتم الاحتفاظ بـ GIL خلال هذا الوقت ، لذلك لا تقوم Python بعمل Allocs على سلاسل أخرى)
  • شوكات جانبية C ++ ، وفي الطفل يقوم بإعداد مرشح seccomp قبل exec
  • جميع عمليات تخصيص الذاكرة التي تحدث بعد الانقسام والتنفيذ المسبق تأتي من libseccomp نفسها ، في seccomp_init إلخ.
  • هناك 4 مصفوفات على وجه التحديد بين استدعاء كود C ++ وإيقاف التشغيل ، كلهم ​​في Cython ولديهم نطاقات مناسبة عند الكتابة إليهم (السطر 435 يستدعي رمز seccomp المرتبط سابقًا) - جميع التخصيصات / عمليات الكتابة الأخرى / إلخ. ضمن مترجم بايثون

أثناء كتابة هذا ، خطرت لي فكرة: لقد سمعت قصص رعب حول 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 :

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L260 -L296

أعتقد أنني اكتشفت مشكلة واحدة على الأقل. في gen_bpf_generate ؛

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1963 -L1966

state.bpf = prgm طالما أن zmalloc لم يفشل. بعد ذلك ، تم استدعاء _gen_bpf_build_bpf ، واستنادًا إلى rc ، تم تعيين state.bpf على NULL .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1968 -L1971

بالنظر إلى الحالة التي يكون فيها rc != 0 ، state.bpf لا يزال مضبوطًا على prgm في وقت المكالمة إلى _state_release . سيؤدي هذا إلى تحرير الذاكرة المشار إليها بـ prgm .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L539

بعد ذلك ، gen_bpf_generate سوف return prgm ، والذي على الرغم من تحريره ، لا يزال مؤشرًا غير صفري.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1971 -L1974

مرة أخرى في sys_filter_load ، gen_bpf_generate عائدات ، و prgm ليس - NULL لذلك يستمر.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L265 -L267

أخيرًا ، في نهاية sys_filter_load ، يتم استدعاء gen_bpf_release على prgm المجاني بالفعل.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L292 -L295

هذا لا يعالج القلق بشأن سبب فشل _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:

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1935 -L1943

نظرًا لطبيعة الاستبدال ، أعتقد أنه يمكننا استبعاد أي EFAULT في أي وظيفة مساعدة ، نظرًا لأن تلك الوظائف كانت ستجهض أولاً.

بعد ذلك ، حاولت إعادة إنتاج نفس الشيء باستخدام HEAD - ما زال يحدث. بعد ذلك ، %s:/goto build_bpf_free_blks/abort() وكرر الأمر. كان السبب:

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L2219 -L2220

لحسن الحظ ، كانت هذه الوظيفة قصيرة ، ولم يكن بها سوى عدد قليل من نقاط الفشل. جولة أخرى من عمليات الإدراج في وقت لاحق بـ 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

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L449 -L452

لذا فقد فشل 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

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات