Libseccomp: BUG: التوافق مع برنامج Openmp

تم إنشاؤها على ٢ سبتمبر ٢٠١٧  ·  19تعليقات  ·  مصدر: seccomp/libseccomp

تجعل الطبيعة غير الحتمية للترابط المتعدد من الصعب تصحيح هذا قابلة للتكرار إلى حد ما seccomp_omp.c.gz . قم بتجميع البرنامج باستخدام -O0 -ggdb -fopenmp .

إذا قمنا بتنفيذها باستخدام مؤشر ترابط واحد فقط وتركناها تقوم بمكالمة syscall المحظورة ، فإن البرنامج سيموت كما هو متوقع.

$ ./seccomp_omp -t 1 -k 0      
86195973
[1]    10103 invalid system call (core dumped)  ./seccomp_omp -t 1 -k 0

ومع ذلك ، نظرًا لمزيد من الخيوط ، فإنها تتوقف عن العمل.

$ ./seccomp_omp -t 2 -k 0
86198868
86195973
86200317
^C

البرنامج لا يقتل ، إنه فقط يتوقف عن فعل أي شيء على الإطلاق. أنا في حيرة من أمر ما حدث لإشارة SIGSYS ، ولماذا لا يتم التعامل معها؟

قد لا تكون هذه مشكلة libseccomp على الإطلاق ، لكنني لست بارعًا بما يكفي مع seccomp والإشارات ونظام مؤشر الترابط بشكل عام لتصحيح السبب الجذري. آمل أن تتمكن من فهمها.

bug prioritmedium

ال 19 كومينتر

ملاحظة: لم ألق نظرة على حالتك الاختبارية حتى الآن ، أنا فقط أخمن بناءً على وصفك المكتوب جيدًا

بالنظر إلى الطبيعة متعددة الخيوط لهذا ، هل حاولت تعيين سمة عامل التصفية SCMP_FLTATR_CTL_TSYNC على true قبل تحميل عامل التصفية في kernel؟

لم أقم بتعيين SCMP_FLTATR_CTL_TSYNC لأنني أضفت عامل تصفية seccomp ، قبل التقسيم إلى سلاسل رسائل. وبالتالي ، يجب أن تقوم النواة بتطبيق المرشح على جميع الخيوط ، على أي حال (ويبدو أنها تفعل ذلك). لا تحدث إضافة SCMP_FLTATR_CTL_TSYNC إلى حقيبة الاختبار أي فرق (وهو أقل من 100 سطر مع معالجة الأخطاء المتحذلق ، راجع للشغل).

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

إن التأكيد على أنني لا أفعل أشياء خاطئة بشكل صارخ هو أمر جيد بما يكفي بالنسبة لي. خذ وقتك!

في المثال ذي الخيوط الواحدة ، ./seccomp_omp -t 1 -k 0 ، يتعرف openmp على أنه سيتم تشغيل مؤشر ترابط واحد فقط ، لذلك يتجاوز openmp الكثير من المزامنة التي كان يفعلها عادةً في حلقة for متعددة الخيوط. لقد تحققت من ذلك من خلال مراقبة عمليات تسجيل الدخول التي تمت معالجتها في seccomp_run_filters () في النواة. كما يتوقع المرء ، رأيت استدعاءًا لـ __NR_write () واستدعاء __NR_madvise () مما دفع seccomp لإصدار تعليمات للنواة لقتل الخيط.

في المثال متعدد الخيوط ، ./seccomp_omp -t 2 -k 0 ، يحاول openmp موازنة حلقة for. وبالتالي يتم استخدام المزيد من مكتبة openmp. يتضح هذا عند مشاهدة syscalls مرة أخرى من خلال seccomp_run_filters (). __NR_mmap () ، __NR_mprotect () ، __NR_clone () ، __NR_futex () ، ويتم استدعاء المزيد من عمليات syscalls قبل استدعاء madvise (). في نهاية المطاف ، يقوم أحد هذين الموضوعين باستدعاء madvise () و seccomp يقتل هذا الخيط بشكل صحيح. لكن openmp لديه مزامنة بين الخيوط وقبل أن يتمكن الخيط الثاني من استدعاء madvise () (ويقتل) ، يستدعي الخيط الثاني futex () لأنه ينتظر شيئًا من الخيط الميت. وهذا هو سبب توقف البرنامج. تم قتل خيط واحد والخيط الثاني ينتظر البيانات (من الخيط الميت) التي لن تصل أبدًا.

لم أعمل كثيرًا مع openmp ، ولكن يبدو أن هناك بعض المناقشات [ 1 ، 2 ، 3 ، ...] حول الإشارات في الكتل المتوازية. في النهاية يبدو أنها مشكلة صعبة سيكون من الأفضل تجنبها.

يبدو أن هناك بعض الحلول السهلة المحتملة:

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

  2. إذا لم تكن بحاجة إلى قوة openmp ، فقد يكون الخيار الآخر هو التبديل إلى استخدام حل أكثر شيوعًا مثل pthread_create () أو fork ()

drakenclimber ، لذا يبدو أن المشكلة هي في الحقيقة أن openmp يقوم بإجراء عمليات تسجيل إضافية والتي قد لا تكون عادةً جزءًا من المرشح؟ أم أن هذا يفتقد إلى نقطة أكبر؟

drakenclimber شكرًا جزيلاً لك على استثمار وقتك. يشرح انتظار الخيط الميت الأعراض.

لإعطاء المزيد من السياق: أكتب كودًا علميًا ، وبالتالي أحب أن أبقي الأمور بسيطة مع OpenMP. ومع ذلك ، أريد أيضًا حماية المستخدمين من أنفسهم. وبالتالي ، إذا كان برنامجي يفعل أي شيء خارج عن المألوف ، فما عليك سوى تفجيره. أظن أن ما أريد تحقيقه في النهاية هو قتل العملية (# 96) برسالة خطأ معقولة إلى حد ما.

ما القيمة التي يجب أن أستخدمها لـ errno في SCMP_ACT_ERROR لتقليد SECCOMP_RET_KILL_PROCESS ؟ هل هناك شيء يعمل بشكل جيد عبر جميع أنظمة تسجيل الدخول؟

pcmoore - كيندا. لكنني أعتقد أن المشكلة الأكبر هي أن openmp لا يتعامل مع الإشارات بأمان داخل بنيته المتوازية. أعتقد أن هذا حسب التصميم.

kloetzl - لا داعي للقلق - يمكنك

ctx = seccomp_init(SCMP_ACT_ERRNO(ENOTBLK));
...
bool kill_process = false;
int rc;
#pragma omp parallel for num_threads(THREADS)
  for (int i = 0; i < THREADS * 2; i++) {
    fprintf(stderr, "%u\n", func(i));
    if (i == K) {
      rc = madvise(NULL, 0, 0); 
      if (rc < 0)
        kill_process = true;
      }   
    // sleep(10);
  }

  if (kill_process)
    goto error;

  seccomp_release(ctx);
  return 0;
error:
  seccomp_release(ctx);
  return -1; 
}

بالنسبة إلى ما يجب أن يعيده الخطأ SCMP_ACT_ERRNO() ، فالأمر متروك لك حقًا. سيعمل SCMP_ACT_ERRNO() عبر جميع أنظمة الاتصال. وسيكون برنامجك هو الذي يتعامل معه ويعيد الخطأ إلى المستخدم ، حتى تتمكن من اختيار ما يناسبك من أخطاء. في الواقع ، قد يكون اختيار رمز غير معروف - على سبيل المثال ENOTBLK - طريقة تعرف أن الخطأ جاء من seccomp الذي يحظر المكالمة.

tl ؛ dr - اكتشف المشكلة في الحلقة المتوازية ، لكن انتظر حتى يتم التعامل معها حتى تكتمل الحلقة. قد تحتاج إلى إضافة ترميز دفاعي إضافي داخل الحلقة بسبب فشل الاتصال.

kloetzl -

وسيكون برنامجك هو الذي يتعامل معه ويعيد الخطأ إلى المستخدم ، حتى تتمكن من اختيار ما يناسبك من أخطاء. في الواقع ، قد يكون اختيار رمز غامض - على سبيل المثال ENOTBLK - طريقة تعرف أن الخطأ جاء من Seccomp الذي يحظر المكالمة.

الشيء هو ، madvise يسمى في أعماق أحشاء malloc . وبالتالي ، أنا لست الشخص الذي يتعامل مع الخطأ ، غليبك. لذا فإن السؤال هو ، هل يعرف glibc (وجميع المكتبات الأخرى التي تقوم بعمليات syscalls) أن syscall يمكنه إرجاع أخطاء أخرى غير تلك الواردة في الصفحة الرئيسية الخاصة به والتعامل معها بشكل مناسب؟

الشيء هو ، يسمى madvise في أعماق أحشاء malloc. وبالتالي ، أنا لست الشخص الذي يتعامل مع الخطأ ، غليبك. لذا فإن السؤال هو ، هل يعرف glibc (وجميع المكتبات الأخرى التي تقوم بعمليات syscalls) أن syscall يمكنه إرجاع أخطاء أخرى غير تلك الواردة في الصفحة الرئيسية الخاصة به والتعامل معها بشكل مناسب؟

آه ... مسكتك. سأضطر إلى البحث في كود glibc للتأكد ، لكنني سأكون على استعداد للمخاطرة بالتخمينات التالية:

  • إذا أرجع madvise خطأ ، فسيعرض glibc _definitely_ أيضًا خطأ
  • من المحتمل بالتأكيد أن يعرض glibc خطأ مختلفًا عن الخطأ الذي تم إرجاعه بواسطة seccomp ، لذا يجب توخي الحذر إذا كنت تتوقع رمز إرجاع "مخصص" مثل ENOTBLK

قصة قصيرة طويلة ، يجب ألا يمنع glibc رمز الخطأ _any_ ، ولكنه قد يعرض رمز خطأ مختلفًا بدلاً من ذلك

أحاول مواكبةكم يا رفاق ، لكنني أخشى أن افتقاري للخلفية مع OpenMP قد جعلني متأخراً قليلاً ... بناءً على التعليقات أعلاه يبدو أن KILL_PROCESS يمكن أن يكون حلاً عمليًا هنا؟ إذا كان الأمر كذلك ، فيمكننا زيادة أولوية هذه المشكلة ، على الرغم من وجودها في قائمة الأشياء التي يجب معالجتها قبل إصدار v2.4.

pcmoore - نعم ، أعتقد أن KILL_PROCESS هو حل قابل للتطبيق لهذا الخطأ. لقد اختبرت برنامج اختبار KILL_PROCESS ولم يعد يحدث التعطل .

لقد تم تنفيذه ولكني أصطدم حاليًا ببعض العوائق في الاختبارات التلقائية للثعبان. يجب أن يكون لدي تصحيح في الأسبوع المقبل.

drakenclimber أوه ، بقع؟ انا احب البقع :)

شكرا يا شباب.

أستطيع أن أؤكد أن KILL_PROCESS يحل المشكلة: لقد اخترقت أيضًا إصدارًا محليًا من libseccomp على Arch وفشل برنامج الاختبار على النحو المنشود. ومع ذلك ، فإن تقديم اختبارات قوية وتشغيلها على نواة مختلفة قد يكون التحدي الأكبر.

شكرا للتأكيد kloetzl.

نعم ، قد تكون كتابة الاختبارات المناسبة مملة ، لكنها مهمة. بنفس أهمية الكود فإنه يختبر IMHO.

أنا أتطلع إلى العلاقات العامة من drakenclimber ، يمكننا مناقشة الأمور أكثر قليلاً بمجرد نشر هذا الرمز.

أنا أقوم ببعض التنظيف الربيعي لـ COVID-19 وأعتقد أننا قمنا بحل مشكلتك ، هل هذا صحيحkloetzl؟ سأغلق هذه المشكلة ، ولكن إذا كنت مخطئًا وما زلت تواجه مشكلات ، فيرجى إخبارنا وسنعيد فتحها!

شكرا لتذكيري ، لقد اختبرت قليلاً من نهايتي.

تم إصلاح هذه المشكلة في الغالب ، مع عقبة صغيرة. لم يعد البرنامج معلقًا في طي النسيان عند مصادفة مكالمة syscall غير صالحة ، رائع! باستخدام SCMP_ACT_TRAP يمكن للمرء أن ينتج اسمًا لمكالمة النظام التالفة. ومع ذلك ، يقوم OpenMP بالكتابة فوق معالج الإشارة الخاص بي ، لذلك يعود إلى رسالة الخطأ الافتراضية بمجرد ظهور العديد من مؤشرات الترابط. من منظور تجربة المستخدم ، لا أحب هذا السلوك ، لكنني أعتقد أنه جيد كما هو.

آه ، نعم ، لست متأكدًا من وجود الكثير مما يمكننا فعله بشأن الكتابة فوق معالج الإشارة في libseccomp ، آسف لذلك.

نشكرك على إخبارنا بأن بقية الأمر نجح!

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