pkeys(7)

الاسم
pkeys - نظرة عامة على مفاتيح حماية الذاكرة

الوصف
مفاتيح حماية الذاكرة (pkeys) هي امتداد لأذونات الذاكرة القائمة على الصفحات. تتطلب أذونات الصفحات العادية باستخدام جداول الصفحات استدعاءات نظام مكلفة وإبطالات TLB عند تغيير الأذونات. توفر مفاتيح حماية الذاكرة آلية لتغيير الحماية دون الحاجة إلى تعديل جداول الصفحات في كل تغيير إذن.

لاستخدام pkeys، يجب على البرنامج أولاً "وسم" صفحة في جداول الصفحات بمفتاح pkey. بعد وضع هذا الوسم، يحتاج التطبيق فقط إلى تغيير محتويات سجل لإزالة إذن الكتابة، أو كل الوصول إلى صفحة موسومة.

تعمل مفاتيح الحماية بالتزامن مع أذونات PROT_READ وPROT_WRITE وPROT_EXEC الحالية الممررة إلى استدعاءات النظام مثل mprotect(2) وmmap(2)، ولكنها تعمل دائمًا على تقييد آليات الإذن التقليدية هذه بشكل أكبر.

إذا نفذت عملية وصولًا ينتهك قيود pkey، فإنها تتلقى إشارة SIGSEGV. راجع sigaction(2) للحصول على تفاصيل المعلومات المتاحة مع تلك الإشارة.

لاستخدام ميزة pkeys، يجب أن يدعمها المعالج، ويجب أن يحتوي النواة على دعم للميزة على معالج معين. اعتبارًا من أوائل عام 2016، يتم دعم معالجات Intel x86 المستقبلية فقط، وتدعم هذه الأجهزة 16 مفتاح حماية في كل عملية. ومع ذلك، يُستخدم مفتاح pkey 0 كمفتاح مبدئي، لذا يتوفر حد أقصى 15 مفتاحًا للاستخدام الفعلي للتطبيق. يُخصص المفتاح المبدئي لأي منطقة ذاكرة لم يُخصص لها مفتاح pkey بشكل صريح عبر pkey_mprotect(2).

لدى مفاتيح الحماية القدرة على إضافة طبقة من الأمان والموثوقية للتطبيقات. لكنها لم تُصمم في المقام الأول كميزة أمان. على سبيل المثال، WRPKRU هي تعليمة غير مميزة تمامًا، لذا فإن pkeys عديمة الفائدة في أي حالة يتحكم فيها المهاجم في سجل PKRU أو يمكنه تنفيذ تعليمات عشوائية.

يجب على التطبيقات أن تكون حذرة جدًا لضمان عدم "تسريب" مفاتيح الحماية. على سبيل المثال، قبل استدعاء pkey_free(2)، يجب على التطبيق التأكد من عدم وجود ذاكرة مخصص لها ذلك المفتاح pkey. إذا ترك التطبيق المفتاح المحرر مخصصًا، فقد يغير مستخدم مستقبلي لذلك المفتاح أذونات بنية بيانات غير مرتبطة عن غير قصد، مما قد يؤثر على الأمان أو الاستقرار. تسمح النواة حاليًا باستدعاء pkey_free(2) على مفاتيح pkey قيد الاستخدام لأنه سيكون له آثار على أداء المعالج أو الذاكرة لإجراء الفحوصات الإضافية اللازمة لمنع ذلك. يُترك تنفيذ الفحوصات اللازمة للتطبيقات. يمكن للتطبيقات تنفيذ هذه الفحوصات بالبحث في ملف /proc/pid/smaps عن مناطق الذاكرة ذات المفتاح pkey المخصص. يمكن العثور على تفاصيل إضافية في proc(5).

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

على الرغم من عدم الحاجة، يمكن تعداد دعم الأجهزة لمفاتيح الحماية باستخدام تعليمة cpuid. يمكن العثور على تفاصيل كيفية القيام بذلك في دليل مطوري برامج Intel. تقوم النواة بهذا التعداد وتكشف المعلومات في /proc/cpuinfo تحت حقل "flags". تشير السلسلة "pku" في هذا الحقل إلى دعم الأجهزة لمفاتيح الحماية وتشير السلسلة "ospke" إلى أن النواة تحتوي على دعم مفاتيح الحماية وقد مكنته.

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

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

سلوك الإشارة هذا غير معتاد ويعود إلى حقيقة أن سجل x86 PKRU (الذي يخزن حقوق الوصول لمفاتيح الحماية) يُدار بنفس آلية الأجهزة (XSAVE) التي تدير سجلات الفاصلة العائمة. سلوك الإشارة هو نفسه سلوك سجلات الفاصلة العائمة.

  استدعاءات نظام مفاتيح الحماية
تنفذ نواة لينكس استدعاءات النظام التالية المتعلقة بـ pkey: pkey_mprotect(2) وpkey_alloc(2) وpkey_free(2).

استدعاءات نظام pkey في لينكس متاحة فقط إذا تم تكوين النواة وبناؤها مع خيار CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS.

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

    $ ./a.out
    buffer contains: 73
    about to read buffer again...
    Segmentation fault (core dumped)

  مصدر البرنامج
    #define _GNU_SOURCE
    #include <err.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/mman.h>

    int
    main(void)
    {
        int status;
        int pkey;
        int *buffer;

        /*
         * Allocate one page of memory.
         */
        buffer = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
                      MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
        if (buffer == MAP_FAILED)
            err(EXIT_FAILURE, "mmap");

        /*
         * Put some random data into the page (still OK to touch).
         */
        *buffer = __LINE__;
        printf("buffer contains: %d\n", *buffer);

        /*
         * Allocate a protection key:
         */
        pkey = pkey_alloc(0, 0);
        if (pkey == -1)
            err(EXIT_FAILURE, "pkey_alloc");

        /*
         * Disable access to any memory with "pkey" set,
         * even though there is none right now.
         */
        status = pkey_set(pkey, PKEY_DISABLE_ACCESS);
        if (status)
            err(EXIT_FAILURE, "pkey_set");

        /*
         * Set the protection key on "buffer".
         * Note that it is still read/write as far as mprotect() is
         * concerned and the previous pkey_set() overrides it.
         */
        status = pkey_mprotect(buffer, getpagesize(),
                               PROT_READ | PROT_WRITE, pkey);
        if (status == -1)
            err(EXIT_FAILURE, "pkey_mprotect");

        printf("about to read buffer again...\n");

        /*
         * This will crash, because we have disallowed access.
         */
        printf("buffer contains: %d\n", *buffer);

        status = pkey_free(pkey);
        if (status == -1)
            err(EXIT_FAILURE, "pkey_free");

        exit(EXIT_SUCCESS);
    }

انظر أيضًا
pkey_alloc(2), pkey_free(2), pkey_mprotect(2), sigaction(2)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.