سؤال

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

أنا قلق بشأن ما يحدث إذا استدعت العملية fork (يمكن أن يكون من خيط آخر ، ويمكن أن يحدث إما في حين أن syscall التي تستدعي get_user_pages قيد التقدم أو في وقت لاحق). على وجه الخصوص ، إذا كتب الوالد إلى منطقة الذاكرة المشتركة بعد الشوكة ، فما الذي أعرفه عن العنوان المادي الأساسي (الذي يُفترض أنه تم تغييره بسبب النسخ على المكاتب)؟ اريد ان افهم:

  1. ما يتعين على kernel فعله للدفاع ضد عملية سوء التصرف المحتملة (لا أريد إنشاء ملف الأمان الفجوة!)؛
  2. ما هي القيود التي تحتاجها العملية إلى الطاعة بحيث وظائف يعمل برنامج التشغيل الخاص بنا بشكل صحيح (أي تظل الذاكرة الفعلية تم تعيينها في نفس العنوان في العملية الأصل).

    • من الناحية المثالية ، أود أن لا تستخدم عملية الطفل سائقنا على الإطلاق (ربما يستدعي exec على الفور تقريبا) للعمل.
    • من الناحية المثالية ، لا ينبغي أن تتخذ العملية الأصل أي خطوات خاصة عند تخصيص الذاكرة ، حيث أن لدينا رمزًا موجودًا يمرر المخزن المؤقت الذي تم تخصيصه إلى برنامج التشغيل.
    • أنا على علم madvise مع MADV_DONTFORK, ، وسيكون من المقبول أن تختفي الذاكرة من مساحة عملية الطفل ، ولكنها لا تنطبق على المخزن المؤقت الذي تم تخصيصه.
    • "لا تستخدم الشوكة بينما يكون لديك اتصال نشط مع سائقنا" سيكون مزعجًا ، ولكنه مقبول كملاذ أخير إذا كانت النقطة 1 راضية.

أنا على استعداد لإشارة إلى الوثائق أو رمز المصدر. لقد نظرت بشكل خاص في برامج تشغيل جهاز Linux, ، ولكن لم تجد هذه المشكلة معالجة. تم تطبيق RTFs على الجزء ذي الصلة من مصدر kernel فقط.

إصدار kernel ليس ثابتًا تمامًا ولكنه إصدار حديث (دعنا نقول ≥2.6.26). نحن نستهدف فقط منصات الذراع (المعالجة الفردية حتى الآن ولكن multirosore هي مجرد زاوية) ، إذا كان الأمر مهمًا.

هل كانت مفيدة؟

المحلول

أ fork() لن تتداخل مع get_user_pages(): get_user_pages() سوف يعطيك أ struct page.

ستحتاج إلى kmap() قبل أن تتمكن من الوصول إليه ، ويتم هذا التعيين في مساحة kernel ، وليس Usserpace.

تعديل: get_user_pages() المس جدول الصفحة ، لكن يجب ألا تشعر بالقلق بشأن هذا (تأكد فقط من تعيين الصفحات في مساحة المستخدمين) ، وإرجاع -fault إذا كان لديها أي مشكلة في القيام بذلك.

إذا كنت تافهة () ، حتى يتم تنفيذ النسخ على الكتابة ، فسيكون الطفل قادرًا على رؤية تلك الصفحة. بمجرد الانتهاء من النسخ على الكتابة (لأن الطفل/السائق/الوالد كتب إلى الصفحة من خلال رسم خرائط للمساحة-وليس kernel kmap () السائق) ، لن تتم مشاركة تلك الصفحة. إذا كنت لا تزال تحمل kmap () على الصفحة (في رمز السائق) ، فلن تتمكن من معرفة ما إذا كنت تحتفظ بصفحة الأصل أو الطفل.

1) إنها ليست ثقبًا أمنيًا ، لأنه بمجرد أن تنفذ () ، فقد ذهب كل ذلك.

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

ماذا عن إضافة بعض الوظائف في مساحة المستخدمين مثل:

 f = open("/dev/your_thing")
 mapping = mmap(f, ...)

عند استدعاء MMAP () على جهازك ، تقوم بتثبيت رسم خرائط للذاكرة ، مع أعلام خاصة:http://os1a.cs.columbia.edu/lxr/source/include/linux/mm.h#071

لديك بعض الأشياء المثيرة للاهتمام مثل:

#define VM_SHARED       0x00000008
#define VM_LOCKED       0x00002000
#define VM_DONTCOPY     0x00020000      /* Do not copy this vma on fork */

سيقوم VM_Shared بتعطيل نسخة على الكتابة vm_locked سوف تعطيل المبادلة على تلك الصفحة سوف تخبر VM_Dontcopy kernel بعدم نسخ منطقة VMA على الشوكة ، على الرغم من أنني لا أعتقد أنها فكرة جيدة

نصائح أخرى

الجواب المختصر هو الاستخدام madvise(addr, len, MADV_DONTFORK) على أي مخازن مؤقتة للمساحة التي تقدمها لسائقك. هذا يخبر النواة أنه لا ينبغي نسخ التعيين من الوالد إلى الطفل وبالتالي لا توجد بقرة.

العيب هو أن الطفل لا يرث أي رسم خرائط على هذا العنوان ، لذلك إذا كنت تريد أن يبدأ الطفل في استخدام برنامج التشغيل ، فسوف يحتاج إلى إعادة تجديد تلك الذاكرة. ولكن هذا سهل إلى حد ما في مساحة المستخدمين.

تحديث: المخزن المؤقت على المكدس هو مشكلة ، لست متأكدًا من أنه يمكنك جعلها آمنة بشكل عام.

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

الطريقة الأخرى لتجنب البقرة هي إنشاء رسم خرائط مشترك ، لكن لا يمكنك تعيين مكدتك المشتركة لأسباب واضحة.

هذا يعني أنك تخاطر بقرة إذا كنت مفترقًا. حتى إذا كان الطفل "Just" execs قد لا يزال يلمس صفحة المكدس ويتسبب في بقرة ، مما يؤدي إلى الحصول على صفحة مختلفة ، وهو أمر سيء.

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

أعتقد أنك تريد حقًا الحصول على كل الذاكرة التي يتم منحها لسائقك لتأتي من مخصص مخصص في مساحة المستخدمين. لا ينبغي أن يكون هذا تدخلي. يمكن للمخصص إما mmap جهازك مباشرة ، كما اقترح الإجابة الأخرى ، أو استخدم مجهول الهوية فقط mmap, madvise(DONTFORK), وربما mlock() لتجنب المبادلة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top