.\" -*- coding: UTF-8 -*- .\" Copyright 2020, Michael Kerrisk .\" Copyright, the authors of the Linux man-pages project .\" .\" SPDX-License-Identifier: Linux-man-pages-copyleft .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH seccomp_unotify 2 "22 فبراير 2026" "صفحات دليل لينكس 6.18" .SH الاسم seccomp_unotify \- آلية إشعار seccomp في فضاء المستخدم .SH المكتبة مكتبة سي المعيارية (\fIlibc\fP،\ \fI\-lc\fP) .SH موجز .nf \fB#include \fP \fB#include \fP \fB#include \fP .P \fBint seccomp(unsigned int \fP\fIoperation\fP\fB, unsigned int \fP\fIflags\fP\fB, void *\fP\fIargs\fP\fB);\fP .P \fB#include \fP .P \fBint ioctl(int \fP\fIfd\fP\fB, SECCOMP_IOCTL_NOTIF_RECV,\fP \fB struct seccomp_notif *\fP\fIreq\fP\fB);\fP \fBint ioctl(int \fP\fIfd\fP\fB, SECCOMP_IOCTL_NOTIF_SEND,\fP \fB struct seccomp_notif_resp *\fP\fIresp\fP\fB);\fP \fBint ioctl(int \fP\fIfd\fP\fB, SECCOMP_IOCTL_NOTIF_ID_VALID, __u64 *\fP\fIid\fP\fB);\fP \fBint ioctl(int \fP\fIfd\fP\fB, SECCOMP_IOCTL_NOTIF_ADDFD,\fP \fB struct seccomp_notif_addfd *\fP\fIaddfd\fP\fB);\fP .fi .SH الوصف .\" تصف هذه الصفحة آلية إشعار فضاء المستخدم التي توفرها وسيلة الحوسبة الآمنة (seccomp). بالإضافة إلى استخدام علامة \fBSECCOMP_FILTER_FLAG_NEW_LISTENER\fP، وقيمة الإجراء \fBSECCOMP_RET_USER_NOTIF\fP، وعملية \fBSECCOMP_GET_NOTIF_SIZES\fP الموصوفة في \fBseccomp\fP(2)، تتضمن هذه الآلية استخدام عدد من عمليات \fBioctl\fP(2) ذات الصلة (الموصوفة أدناه). .SS "نظرة عامة" في الاستخدام التقليدي لمرشح seccomp، يُتخذ القرار بشأن كيفية التعامل مع استدعاء النظام بواسطة المرشح نفسه. على النقيض من ذلك، تسمح آلية إشعار فضاء المستخدم لمرشح seccomp بتفويض معالجة استدعاء النظام إلى عملية أخرى في فضاء المستخدم. لاحظ أن هذه الآلية \fBليست\fP مخصصة صراحةً كطريقة لتنفيذ سياسة أمنية؛ انظر الملاحظات (NOTES). .P في المناقشة التي تلي، يُشار إلى الخيط (أو الخيوط) التي ثُبت عليها مرشح seccomp باسم \fIالهدف\fP (target)، ويُشار إلى العملية التي تُخطر بواسطة آلية إشعار فضاء المستخدم باسم \fIالمشرف\fP (supervisor). .P يمكن للمشرف الذي يتمتع بامتيازات مناسبة استخدام آلية إشعار فضاء المستخدم لتنفيذ إجراءات نيابة عن الهدف. تكمن ميزة آلية إشعار فضاء المستخدم في أن المشرف سيكون قادرًا عادةً على استرجاع معلومات حول الهدف واستدعاء النظام المنفذ التي لا يستطيع مرشح seccomp نفسه الوصول إليها. (مرشح seccomp محدود في المعلومات التي يمكنه الحصول عليها والإجراءات التي يمكنه القيام بها لأنه يعمل على جهاز افتراضي داخل النواة.) .P .\"------------------------------------- نظرة عامة على الخطوات التي يقوم بها كل من الهدف والمشرف هي كما يلي: .IP (1) 5 ينشئ الهدف مرشح seccomp بالطريقة المعتادة، ولكن مع اختلافين: .RS .IP \[bu] 3 .\" FIXME .\" Is the last sentence above correct? .\" .\" Kees Cook (25 Oct 2020) notes: .\" .\" I like this limitation, but I expect that it'll need to change in the .\" future. Even with LSMs, we see the need for arbitrary stacking, and the .\" idea of there being only 1 supervisor will eventually break down. Right .\" now there is only 1 because only container managers are using this .\" feature. But if some daemon starts using it to isolate some thread, .\" suddenly it might break if a container manager is trying to listen to it .\" too, etc. I expect it won't be needed soon, but I do think it'll change. .\" تتضمن وسيطة \fIflags\fP في \fBseccomp\fP(2) العلامة \fBSECCOMP_FILTER_FLAG_NEW_LISTENER\fP. وبناءً على ذلك، تكون القيمة المعادة من استدعاء \fBseccomp\fP(2) (الناجح) هي واصف ملف "استماع" جديد يمكن استخدامه لتلقي الإشعارات. يمكن تثبيت مرشح seccomp "مستمع" واحد فقط لكل خيط. .IP \[bu] في الحالات المناسبة، يعيد مرشح seccomp قيمة الإجراء \fBSECCOMP_RET_USER_NOTIF\fP. ستؤدي هذه القيمة المعادة إلى إطلاق حدث إشعار. .RE .\"------------------------------------- .IP (2) .\" Jann Horn: .\" Instead of using unix domain sockets to send the fd to the .\" parent, I think you could also use clone3() with .\" flags==CLONE_FILES|SIGCHLD, dup2() the seccomp fd to an fd .\" that was reserved in the parent, call unshare(CLONE_FILES) .\" in the child after setting up the seccomp fd, and wake .\" up the parent with something like pthread_cond_signal()? .\" I'm not sure whether that'd look better or worse in the .\" end though, so maybe just ignore this comment. .\"------------------------------------- لكي يتمكن المشرف من الحصول على الإشعارات باستخدام واصف ملف الاستماع، يجب تمرير (نسخة من) واصف الملف هذا من الهدف إلى المشرف. إحدى الطرق التي يمكن من خلالها القيام بذلك هي تمرير واصف الملف عبر اتصال مقبس نطاق يونكس بين الهدف والمشرف (باستخدام نوع الرسالة الملحقة \fBSCM_RIGHTS\fP الموصوفة في \fBunix\fP(7)). طريقة أخرى للقيام بذلك هي من خلال استخدام \fBpidfd_getfd\fP(2). .IP (3) .\"------------------------------------- سيتلقى المشرف أحداث الإشعار على واصف ملف الاستماع. تُعاد هذه الأحداث في هيئة هياكل من نوع \fIseccomp_notif\fP. ولأن هذا الهيكل وحجمه قد يتطوران عبر إصدارات النواة، يجب على المشرف أولاً تحديد حجم هذا الهيكل باستخدام عملية \fBSECCOMP_GET_NOTIF_SIZES\fP في \fBseccomp\fP(2)، والتي تعيد هيكلًا من نوع \fIseccomp_notif_sizes\fP. يخصص المشرف مخزنًا مؤقتًا بحجم \fIseccomp_notif_sizes.seccomp_notif\fP بايت لتلقي أحداث الإشعارات. بالإضافة إلى ذلك، يخصص المشرف مخزنًا مؤقتًا آخر بحجم \fIseccomp_notif_sizes.seccomp_notif_resp\fP بايت للاستجابة (هيكل \fIstruct seccomp_notif_resp\fP) التي سيقدمها للنواة (وبالتالي للهدف). .IP (4) .\"------------------------------------- ثم ينفذ الهدف عبء عمله، والذي يتضمن استدعاءات النظام التي سيتحكم فيها مرشح seccomp. كلما تسبب أحد استدعاءات النظام هذه في إعادة المرشح لقيمة الإجراء \fBSECCOMP_RET_USER_NOTIF\fP، فإن النواة \fIلا\fP تنفذ (بعد) استدعاء النظام؛ وبدلاً من ذلك، يُحجب تنفيذ الهدف مؤقتًا داخل النواة (في حالة نوم قابلة للمقاطعة بواسطة الإشارات) ويُولد حدث إشعار على واصف ملف الاستماع. .IP (5) .\" FIXME .\" Christian Brauner: .\" .\" Do we support O_NONBLOCK with SECCOMP_IOCTL_NOTIF_RECV and if .\" not should we? .\" .\" Michael Kerrisk: .\" .\" A quick test suggests that O_NONBLOCK has no effect on the blocking .\" behavior of SECCOMP_IOCTL_NOTIF_RECV. يمكن للمشرف الآن مراقبة واصف ملف الاستماع بشكل متكرر للأحداث التي تطلقها \fBSECCOMP_RET_USER_NOTIF\fP. للقيام بذلك، يستخدم المشرف عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP في \fBioctl\fP(2) لقراءة معلومات حول حدث إشعار؛ تُحجب هذه العملية حتى يتوفر حدث. تعيد العملية هيكل \fIseccomp_notif\fP يحتوي على معلومات حول استدعاء النظام الذي يحاول الهدف تنفيذه. (كما هو موضح في الملاحظات (NOTES)، يمكن أيضًا مراقبة واصف الملف باستخدام \fBselect\fP(2) أو \fBpoll\fP(2) أو \fBepoll\fP(7).) . .\"------------------------------------- .IP (6) يتضمن هيكل \fIseccomp_notif\fP الذي تعيده عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP نفس المعلومات (هيكل \fIseccomp_data\fP) التي مُررت إلى مرشح seccomp. تسمح هذه المعلومات للمشرف باكتشاف رقم استدعاء النظام ووسائط استدعاء نظام الهدف. بالإضافة إلى ذلك، يحتوي حدث الإشعار على معرف الخيط الذي أطلق الإشعار وقيمة "كعكة" (cookie) فريدة تُستخدم في عمليتي \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP و \fBSECCOMP_IOCTL_NOTIF_SEND\fP اللاحقتين. .IP .\" Tycho Andersen mentioned that there are alternatives to /proc/PID/mem, .\" such as ptrace() and /proc/PID/map_files .\"------------------------------------- يمكن استخدام المعلومات الواردة في الإشعار لاكتشاف قيم وسائط المؤشر لاستدعاء نظام الهدف. (هذا شيء لا يمكن القيام به من داخل مرشح seccomp.) إحدى الطرق التي يمكن للمشرف من خلالها القيام بذلك هي فتح ملف \fI/proc/\fPtid\fI/mem\fP المقابل (انظر \fBproc\fP(5)) وقراءة البايتات من الموقع الذي يقابل أحد وسائط المؤشر التي زُودت قيمتها في حدث الإشعار. (يجب على المشرف توخي الحذر لتجنب حالة التسابق التي يمكن أن تحدث عند القيام بذلك؛ انظر وصف عملية \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP في \fBioctl\fP(2) أدناه.) بالإضافة إلى ذلك، يمكن للمشرف الوصول إلى معلومات النظام الأخرى التي تظهر في فضاء المستخدم ولكن لا يمكن الوصول إليها من مرشح seccomp. .IP (7) بعد الحصول على المعلومات كما في الخطوة السابقة، قد يختار المشرف بعد ذلك تنفيذ إجراء استجابةً لاستدعاء نظام الهدف (والذي، كما لوحظ أعلاه، لا يُنفذ عندما يعيد مرشح seccomp قيمة الإجراء \fBSECCOMP_RET_USER_NOTIF\fP). .IP .\"------------------------------------- أحد أمثلة حالات الاستخدام هنا يتعلق بالحاويات. قد يكون الهدف موجودًا داخل حاوية حيث لا يملك قدرات كافية لوصل نظام ملفات في مساحة اسم الوصل الخاصة بالحاوية. ومع ذلك، قد يكون المشرف عملية ذات امتيازات أعلى تملك القدرات الكافية لتنفيذ عملية الوصل. .IP (8) يرسل المشرف بعد ذلك استجابة للإشعار. تستخدم النواة المعلومات الواردة في هذه الاستجابة لبناء قيمة معادة لاستدعاء نظام الهدف وتوفير قيمة ستُسند إلى متغير \fIerrno\fP الخاص بالهدف. .IP .\"------------------------------------- تُرسل الاستجابة باستخدام عملية \fBSECCOMP_IOCTL_NOTIF_SEND\fP في \fBioctl\fP(2)، والتي تُستخدم لنقل هيكل \fIseccomp_notif_resp\fP إلى النواة. يتضمن هذا الهيكل قيمة الكعكة التي حصل عليها المشرف في هيكل \fIseccomp_notif\fP الذي أعادته عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP. تسمح قيمة الكعكة هذه للنواة بربط الاستجابة بالهدف. يجب أن يتضمن هذا الهيكل قيمة الكعكة التي حصل عليها المشرف في هيكل \fIseccomp_notif\fP الذي أعادته عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP؛ تسمح الكعكة للنواة بربط الاستجابة بالهدف. .IP (9) .\"------------------------------------- بمجرد إرسال الإشعار، يُلغى حجب استدعاء النظام في خيط الهدف، ويعيد المعلومات التي قدمها المشرف في استجابة الإشعار. .P .\" كبديل للخطوتين الأخيرتين، يمكن للمشرف إرسال استجابة تخبر النواة بوجوب تنفيذ استدعاء نظام خيط الهدف؛ انظر مناقشة \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP أدناه. .SH "عمليات IOCTL" .\" عمليات \fBioctl\fP(2) التالية مدعومة من قبل واصف ملف إشعار فضاء مستخدم seccomp. لكل من هذه العمليات، تكون الوسيطة الأولى (واصف الملف) لـ \fBioctl\fP(2) هي واصف ملف الاستماع الذي أعاده استدعاء \fBseccomp\fP(2) مع علامة \fBSECCOMP_FILTER_FLAG_NEW_LISTENER\fP. .SS SECCOMP_IOCTL_NOTIF_RECV تُستخدم عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP (متوفرة منذ لينكس 5.0) للحصول على حدث إشعار في فضاء المستخدم. إذا لم يكن هناك مثل هذا الحدث معلقًا حاليًا، تُحجب العملية حتى يقع حدث. الوسيطة الثالثة لـ \fBioctl\fP(2) هي مؤشر لهيكل بالشكل التالي يحتوي على معلومات حول الحدث. يجب تصفير هذا الهيكل قبل الاستدعاء. .P .in +4n .EX struct seccomp_notif { __u64 id; /* كعكة */ __u32 pid; /* TID لخيط الهدف */ __u32 flags; /* غير مستخدم حاليا (0) */ struct seccomp_data data; /* انظر seccomp(2) */ }; .EE .in .P الحقول في هذا الهيكل هي كما يلي: .TP \fIid\fP هذه هي كعكة للإشعار. يُضمن أن تكون كل كعكة من هذا القبيل فريدة لمرشح seccomp المقابل. .RS .IP \[bu] 3 يمكن استخدام الكعكة مع عملية \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP في \fBioctl\fP(2) الموصوفة أدناه. .IP \[bu] عند إرجاع استجابة إشعار إلى النواة، يجب على المشرف تضمين قيمة الكعكة في هيكل \fIseccomp_notif_resp\fP المحدد كوسيطة لعملية \fBSECCOMP_IOCTL_NOTIF_SEND\fP. .RE .TP \fIpid\fP هذا هو معرف الخيط لخيط الهدف الذي أطلق حدث الإشعار. .TP \fIflags\fP هذا قناع بتات للعلامات التي توفر معلومات إضافية حول الحدث. في التنفيذ الحالي، هذا الحقل دائمًا صفر. .TP \fIdata\fP هذا هيكل \fIseccomp_data\fP يحتوي على معلومات حول استدعاء النظام الذي أطلق الإشعار. هذا هو نفس الهيكل الذي يُمرر إلى مرشح seccomp. انظر \fBseccomp\fP(2) للحصول على تفاصيل حول هذا الهيكل. .P عند النجاح، تعيد هذه العملية 0؛ وعند الفشل، تُعاد \-1، ويُضبط \fIerrno\fP للإشارة إلى الخطأ. يمكن أن تفشل هذه العملية بالأخطاء التالية: .TP \fBEINVAL\fP (منذ لينكس 5.5) .\" commit 2882d53c9c6f3b8311d225062522f03772cf0179 هيكل \fIseccomp_notif\fP الذي مُرر إلى الاستدعاء احتوى على حقول غير صفرية. .TP \fBENOENT\fP .\" FIXME .\" From my experiments, .\" it appears that if a SECCOMP_IOCTL_NOTIF_RECV is done after .\" the target thread terminates, then the ioctl() simply .\" blocks (rather than returning an error to indicate that the .\" target no longer exists). .\" .\" I found that surprising, and it required some contortions in .\" the example program. It was not possible to code my SIGCHLD .\" handler (which reaps the zombie when the worker/target .\" terminates) to simply set a flag checked in the main .\" handleNotifications() loop, since this created an .\" unavoidable race where the child might terminate just after .\" I had checked the flag, but before I blocked (forever!) in the .\" SECCOMP_IOCTL_NOTIF_RECV operation. Instead, I had to code .\" the signal handler to simply call _exit(2) in order to .\" terminate the parent process (the supervisor). .\" .\" Is this expected behavior? It seems to me rather .\" desirable that SECCOMP_IOCTL_NOTIF_RECV should give an error .\" if the target has terminated. .\" .\" Jann posted a patch to rectify this, but there was no response .\" (Lore link: https://bit.ly/3jvUBxk) to his question about fixing .\" this issue. (I've tried building with the patch, but encountered .\" an issue with the target process entering D state after a signal.) .\" .\" For now, this behavior is documented in BUGS. .\" .\" Kees Cook commented: Let's change [this] ASAP! .\" قُتل خيط الهدف بواسطة إشارة أثناء إنشاء معلومات الإشعار، أو قوطع استدعاء نظام الهدف (المحجوب) بواسطة معالج إشارة. .SS SECCOMP_IOCTL_NOTIF_ID_VALID تُستخدم عملية \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP (متوفرة منذ لينكس 5.0) للتحقق من أن معرف الإشعار الذي أعادته عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP سابقة لا يزال صالحًا (أي أن الهدف لا يزال موجودًا واستدعاء نظامه لا يزال محجوبًا في انتظار استجابة). .P الوسيطة الثالثة لـ \fBioctl\fP(2) هي مؤشر للكعكة (\fIid\fP) التي أعادتها عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP. .P هذه العملية ضرورية لتجنب حالات التسابق التي يمكن أن تحدث عندما ينتهي الـ \fIpid\fP الذي أعادته عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP، ويُعاد استخدام معرف العملية هذا بواسطة عملية أخرى. مثال على هذا النوع من التسابق هو ما يلي .IP (1) 5 يُنشأ إشعار على واصف ملف الاستماع. يحتوي هيكل \fIseccomp_notif\fP المُرجع على معرف الخيط (TID) للخيط الهدف (في حقل \fIpid\fP الخاص بالهيكل). .IP (2) ينتهي الهدف. .IP (3) يُنشأ خيط أو عملية أخرى على النظام تُعيد استخدام TID الذي حُرّر عند انتهاء الهدف عن طريق المصادفة. .IP (4) يفتح المشرف عبر \fBopen\fP(2) ملف \fI/proc/\fPtid\fI/mem\fP لـ TID الذي حُصل عليه في الخطوة 1، بقصد فحص مواقع الذاكرة التي تحتوي على وسيط (أو وسائط) استدعاء النظام الذي أطلق الإشعار في الخطوة 1. .P .\" Jann Horn: .\" the PID can be reused, but the /proc/$pid directory is .\" internally not associated with the numeric PID, but, .\" conceptually speaking, with a specific incarnation of the .\" PID, or something like that. (Actually, it is associated .\" with the "struct pid", which is not reused, instead of the .\" numeric PID. في السيناريو أعلاه، يكمن الخطر في أن المشرف قد يحاول الوصول إلى ذاكرة عملية أخرى غير الهدف. يمكن تجنب حالة السباق هذه بإتباع استدعاء \fBopen\fP(2) بعملية \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP للتحقق من أن العملية التي أنشأت الإشعار لا تزال حية. (لاحظ أنه إذا انتهى الهدف بعد الخطوة الأخيرة، فإن أي \fBread\fP(2) لاحقة من واصف الملف قد تُرجع 0، مما يشير إلى نهاية الملف). .P انظر الملاحظات لمناقشة الحالات الأخرى التي يجب فيها إجراء فحوصات \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP. .P .\" عند النجاح (أي أن معرف الإشعار لا يزال صالحاً)، تُرجع هذه العملية 0. وعند الفشل (أي أن معرف الإشعار لم يعد صالحاً)، يُرجع \-1، ويُضبط \fIerrno\fP على \fBENOENT\fP. .SS SECCOMP_IOCTL_NOTIF_SEND تُستخدم عملية \fBSECCOMP_IOCTL_NOTIF_SEND\fP (المتوفرة منذ لينكس 5.0) لإرسال رد إشعار إلى النواة. وسيط \fBioctl\fP(2) الثالث لهذا الهيكل هو مؤشر إلى هيكل بالشكل التالي: .P .in +4n .EX struct seccomp_notif_resp { __u64 id; /* قيمة الكوكيز */ __s64 val; /* قيمة النجاح المرجعة */ __s32 error; /* 0 (نجاح) أو رقم خطأ سالب */ __u32 flags; /* انظر أدناه */ }; .EE .in .P حقول هذا الهيكل هي كالتالي: .TP \fIid\fP هذه هي قيمة الكوكيز التي حُصل عليها باستخدام عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP. تسمح قيمة الكوكيز هذه للنواة بربط هذا الرد بشكل صحيح مع استدعاء النظام الذي أطلق إشعار مساحة المستخدم. .TP \fIval\fP هذه هي القيمة التي ستُستخدم لرد نجاح زائف لاستدعاء نظام الهدف؛ انظر أدناه. .TP \fIerror\fP هذه هي القيمة التي ستُستخدم كرقم خطأ (\fIerrno\fP) لرد خطأ زائف لاستدعاء نظام الهدف؛ انظر أدناه. .TP \fIflags\fP هذا قناع بتات يتضمن صفراً أو أكثر من اللصائق التالية: .RS .TP \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP (منذ لينكس 5.5) .\" commit fb3c5386b382d4097476ce9647260fc89b34afdb إخبار النواة بتنفيذ استدعاء نظام الهدف. .RE .P يوجد نوعان ممكنان من الردود: .IP \[bu] 3 رد للنواة يخبرها بتنفيذ استدعاء نظام الهدف. في هذه الحالة، يتضمن حقل \fIflags\fP العلامة \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP ويجب أن يكون الحقلان \fIerror\fP و \fIval\fP صفرين. .IP هذا النوع من الردود يكون مفيداً في الحالات التي يحتاج فيها المشرف إلى إجراء تحليل أعمق لاستدعاء نظام الهدف مما هو ممكن من مرشح seccomp (مثل فحص قيم وسائط المؤشرات)، وبعد أن يقرر المشرف أن استدعاء النظام لا يتطلب محاكاة، يريد المشرف تنفيذ استدعاء النظام بشكل طبيعي في الهدف. .IP يجب استخدام وسم \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP بحذر؛ انظر الملاحظات. .IP \[bu] قيمة مرجعة زائفة لاستدعاء نظام الهدف. في هذه الحالة، لا تنفذ النواة استدعاء نظام الهدف، وبدلاً من ذلك تجعل استدعاء النظام يُرجع قيمة زائفة كما هو محدد في حقول هيكل \fIseccomp_notif_resp\fP. يجب على المشرف ضبط حقول هذا الهيكل كما يلي: .RS .IP \[bu] 3 لا يحتوي \fIflags\fP على \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP. .IP \[bu] يُضبط \fIerror\fP إما على 0 لرد "نجاح" زائف أو على رقم خطأ سالب لرد "فشل" زائف. في الحالة الأولى، تجعل النواة استدعاء نظام الهدف يُرجع القيمة المحددة في حقل \fIval\fP. وفي الحالة الثانية، تجعل النواة استدعاء نظام الهدف يُرجع \-1، ويُسند إلى \fIerrno\fP قيمة \fIerror\fP المنفية. .IP \[bu] .\" FIXME .\" Kees Cook suggested: .\" .\" Strictly speaking, this is architecture specific, but .\" all architectures do it this way. Should seccomp enforce .\" val == 0 when err != 0 ? .\" .\" Christian Brauner .\" .\" Feels like it should, at least for the SEND ioctl where we already .\" verify that val and err are both 0 when CONTINUE is specified (as you .\" pointed out correctly above). يُضبط \fIval\fP على قيمة ستُستخدم كقيمة مرجعة لرد "نجاح" زائف لاستدعاء نظام الهدف. تُتجاهل القيمة في هذا الحقل إذا احتوى حقل \fIerror\fP على قيمة غير صفرية. .RE .P عند النجاح، تعيد هذه العملية 0؛ وعند الفشل، تُعاد \-1، ويُضبط \fIerrno\fP للإشارة إلى الخطأ. يمكن أن تفشل هذه العملية بالأخطاء التالية: .TP \fBEINPROGRESS\fP أُرسل رد على هذا الإشعار بالفعل. .TP \fBEINVAL\fP حُددت قيمة غير صالحة في حقل \fIflags\fP. .TP \fBEINVAL\fP احتوى حقل \fIflags\fP على \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP، ولم يكن حقل \fIerror\fP أو \fIval\fP صفراً. .TP \fBENOENT\fP .\" Jann Horn notes: .\" you could also get this [ENOENT] if a response has already .\" been sent, instead of EINPROGRESS - the only difference is .\" whether the target thread has picked up the response yet .\" قوطع استدعاء النظام المحجوب في الهدف بواسطة معالج إشارة أو أن الهدف قد انتهى. .SS SECCOMP_IOCTL_NOTIF_ADDFD تسمح عملية \fBSECCOMP_IOCTL_NOTIF_ADDFD\fP (المتوفرة منذ لينكس 5.9) للمشرف بتثبيت واصف ملف في جدول واصفات ملفات الهدف. تشبه هذه العملية إلى حد كبير استخدام رسائل \fBSCM_RIGHTS\fP الموصوفة في \fBunix\fP(7)، وهي مكافئة دلالياً لنسخ واصف ملف من جدول واصفات ملفات المشرف إلى جدول واصفات ملفات الهدف. .P تسمح عملية \fBSECCOMP_IOCTL_NOTIF_ADDFD\fP للمشرف بمحاكاة استدعاء نظام للهدف (مثل \fBsocket\fP(2) أو \fBopenat\fP(2)) الذي يُولد واصف ملف. يمكن للمشرف تنفيذ استدعاء النظام الذي يُولد واصف الملف (ووصف الملف المفتوح المرتبط به) ثم استخدام هذه العملية لتخصيص واصف ملف يشير إلى نفس وصف الملف المفتوح في الهدف. (لشرح أوصاف الملفات المفتوحة، انظر \fBopen\fP(2)). .P بمجرد تنفيذ هذه العملية، يمكن للمشرف إغلاق نسخته من واصف الملف. .P في الهدف، يخضع واصف الملف المستلم لنفس فحوصات وحدة أمن لينكس (LSM) التي تُطبق على واصف ملف مستلم في رسالة \fBSCM_RIGHTS\fP إضافية. إذا كان واصف الملف يشير إلى مقبس، فإنه يرث إعدادات متحكم الشبكة للمجموعة التقسيمية (cgroup) الإصدار 1 (\fIclassid\fP و \fInetprioidx\fP) الخاصة بالهدف. .P وسيط \fBioctl\fP(2) الثالث هو مؤشر إلى هيكل بالشكل التالي: .P .in +4n .EX struct seccomp_notif_addfd { __u64 id; /* قيمة الكوكيز */ __u32 flags; /* اللصائق */ __u32 srcfd; /* رقم واصف الملف المحلي */ __u32 newfd; /* 0 أو رقم واصف الملف المطلوب في الهدف */ __u32 newfd_flags; /* اللصائق المراد ضبطها على واصف ملف الهدف */ }; .EE .in .P الحقول في هذا الهيكل هي كما يلي: .TP \fIid\fP يجب ضبط هذا الحقل على معرف الإشعار (قيمة الكوكيز) التي حُصل عليها عبر \fBSECCOMP_IOCTL_NOTIF_RECV\fP. .TP \fIflags\fP هذا الحقل عبارة عن قناع بتات من اللصائق التي تعدل سلوك العملية. حالياً، تُدعم لصيقة واحدة فقط: .RS .TP \fBSECCOMP_ADDFD_FLAG_SETFD\fP عند تخصيص واصف الملف في الهدف، استخدم رقم واصف الملف المحدد في حقل \fInewfd\fP. .TP \fBSECCOMP_ADDFD_FLAG_SEND\fP (منذ لينكس 5.14) .\" commit 0ae71c7720e3ae3aabd2e8a072d27f7bd173d25c تنفيذ ما يعادل \fBSECCOMP_IOCTL_NOTIF_ADDFD\fP بالإضافة إلى \fBSECCOMP_IOCTL_NOTIF_SEND\fP كعملية ذرية. عند الاستدعاء الناجح، سيكون \fIerrno\fP لعملية الهدف 0 وستكون القيمة المرجعة هي رقم واصف الملف الذي خُصص في الهدف. إذا فشل تخصيص واصف الملف في الهدف، يستمر حجب استدعاء نظام الهدف حتى يُرسل رد ناجح. .RE .TP \fIsrcfd\fP يجب ضبط هذا الحقل على رقم واصف الملف في المشرف المراد نسخه. .TP \fInewfd\fP يحدد هذا الحقل رقم واصف الملف الذي يُخصص في الهدف. إذا ضُبطت الوسم \fBSECCOMP_ADDFD_FLAG_SETFD\fP، فإن هذا الحقل يحدد رقم واصف الملف الذي يجب تخصيصه. إذا كان رقم واصف الملف هذا مفتوحاً بالفعل في الهدف، فسيُغلق ويُعاد استخدامه ذرياً. إذا فشل نسخ الواصف بسبب فحص LSM، أو إذا لم يكن \fIsrcfd\fP واصف ملف صالحاً، فلن يُغلق واصف الملف \fInewfd\fP في عملية الهدف. .IP إذا لم تُضبط الوسم \fBSECCOMP_ADDFD_FLAG_SETFD\fP، فيجب أن يكون هذا الحقل 0، وتقوم النواة بتخصيص أقل رقم واصف ملف غير مستخدم في الهدف. .TP \fInewfd_flags\fP هذا الحقل عبارة عن قناع بتات يحدد اللصائق التي يجب ضبطها على واصف الملف المستلم في عملية الهدف. حالياً، تُنفذ اللصيقة التالية فقط: .RS .TP \fBO_CLOEXEC\fP ضبط وسم الغلق عند التنفيذ على واصف الملف المستلم. .RE .P عند النجاح، يُرجع استدعاء \fBioctl\fP(2) هذا رقم واصف الملف الذي خُصص في الهدف. بافتراض أن استدعاء النظام الذي تمت محاكاته هو استدعاء يُرجع واصف ملف كتيجة لدالته (مثل \fBsocket\fP(2))، يمكن استخدام هذه القيمة كقيمة مرجعة (\fIresp.val\fP) التي تُوفر في الرد الذي يُرسل لاحقًا بعملية \fBSECCOMP_IOCTL_NOTIF_SEND\fP. .P عند حدوث خطأ، يُرجع \-1 ويُضبط \fIerrno\fP للإشارة إلى الخطأ. .P يمكن أن تفشل هذه العملية بالأخطاء التالية: .TP \fBEBADF\fP سيؤدي تخصيص واصف الملف في الهدف إلى تجاوز حد \fBRLIMIT_NOFILE\fP للهدف (انظر \fBgetrlimit\fP(2)). .TP \fBEBUSY\fP إذا استُخدمت اللصيقة \fBSECCOMP_IOCTL_NOTIF_SEND\fP، فهذا يعني أن العملية لا يمكن أن تستمر حتى تُعالج طلبات \fBSECCOMP_IOCTL_NOTIF_ADDFD\fP الأخرى. .TP \fBEINPROGRESS\fP إشعار مساحة المستخدم المحدد في حقل \fIid\fP موجود ولكنه لم يُجلب بعد (بواسطة \fBSECCOMP_IOCTL_NOTIF_RECV\fP) أو تم الرد عليه بالفعل (بواسطة \fBSECCOMP_IOCTL_NOTIF_SEND\fP). .TP \fBEINVAL\fP حُددت لصيقة غير صالحة في حقل \fIflags\fP أو \fInewfd_flags\fP، أو أن حقل \fInewfd\fP غير صفري ولم تُحدد اللصيقة \fBSECCOMP_ADDFD_FLAG_SETFD\fP في حقل \fIflags\fP. .TP \fBEMFILE\fP يتجاوز رقم واصف الملف المحدد في \fInewfd\fP الحد المحدد في \fI/proc/sys/fs/nr_open\fP. .TP \fBENOENT\fP قوطع استدعاء النظام المحجوب في الهدف بواسطة معالج إشارة أو أن الهدف قد انتهى. .P إليك بعض التعليمات البرمجية النموذجية (مع حذف معالجة الأخطاء) التي تستخدم عملية \fBSECCOMP_ADDFD_FLAG_SETFD\fP (هنا، لمحاكاة استدعاء لـ \fBopenat\fP(2)): .P .EX .in +4n int fd, removeFd; \& fd = openat(req\->data.args[0], path, req\->data.args[2], req\->data.args[3]); \& struct seccomp_notif_addfd addfd; addfd.id = req\->id; /* الكوكيز من SECCOMP_IOCTL_NOTIF_RECV */ addfd.srcfd = fd; addfd.newfd = 0; addfd.flags = 0; addfd.newfd_flags = O_CLOEXEC; \& targetFd = ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd); \& close(fd); /* لم تعد هناك حاجة إليه في المشرف */ \& struct seccomp_notif_resp *resp; /* كود تخصيص 'resp' محذوف */ resp\->id = req\->id; resp\->error = 0; /* "نجاح" */ resp\->val = targetFd; resp\->flags = 0; ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_SEND, resp); .in .EE .SH ملاحظات .\" أحد الأمثلة على حالة استخدام آلية إشعار مساحة المستخدم هو السماح لمدير الحاوية (عملية تعمل عادةً بامتيازات أكثر من العمليات داخل الحاوية) بوصل الأجهزة الكتلية أو إنشاء عقد الأجهزة للحاوية. توفر حالة استخدام الوصل مثالاً على فائدة عملية \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP \fBioctl\fP(2). عند استلام إشعار لاستدعاء النظام \fBmount\fP(2)، يمكن لمدير الحاوية ("المشرف") التمييز بين طلب لوصل نظام ملفات كتلي (والذي لن يكون ممكناً لعملية "هدف" داخل الحاوية) ووصل نظام الملفات هذا. ومن ناحية أخرى، إذا اكتشف مدير الحاوية أن العملية يمكن تنفيذها بواسطة العملية داخل الحاوية (على سبيل المثال، وصل نظام ملفات \fBtmpfs\fP(5))، فيمكنه إخطار النواة بأن استدعاء نظام \fBmount\fP(2) لعملية الهدف يمكن أن يستمر. .SS "دلالات select()/poll()/epoll" يمكن مراقبة واصف الملف المعاد عندما يُوظف \fBseccomp\fP(2) مع العلم \fBSECCOMP_FILTER_FLAG_NEW_LISTENER\fP باستخدام \fBpoll\fP(2) و \fBepoll\fP(7) و \fBselect\fP(2). تشير هذه الواجهات إلى أن واصف الملف جاهز كما يلي: .IP \[bu] 3 عندما يكون الإشعار معلقاً، تشير هذه الواجهات إلى أن واصف الملف قابل للقراءة. وعقب هذا المؤشر، لن تحظر عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP \fBioctl\fP(2) اللاحقة، حيث ستعيد إما معلومات حول الإشعار أو تفشل مع الخطأ \fBEINTR\fP إذا قُتل الهدف بواسطة إشارة أو قوطع استدعاء النظام الخاص به بواسطة معالج إشارة. .IP \[bu] بعد تلقي الإشعار (أي بواسطة عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP \fBioctl\fP(2))، تشير هذه الواجهات إلى أن واصف الملف قابل للكتابة، مما يعني أنه يمكن إرسال استجابة إشعار باستخدام عملية \fBSECCOMP_IOCTL_NOTIF_SEND\fP \fBioctl\fP(2). .IP \[bu] بعد إنهاء آخر خيط يستخدم المرشح وحصده باستخدام \fBwaitpid\fP(2) (أو ما شابه)، يشير واصف الملف إلى حالة نهاية الملف (قابل للقراءة في \fBselect\fP(2)؛ و \fBPOLLHUP\fP/\fBEPOLLHUP\fP في \fBpoll\fP(2)/ \fBepoll_wait\fP(2)). .SS "أهداف التصميم؛ استخدام SECCOMP_USER_NOTIF_FLAG_CONTINUE" الغرض من ميزة إشعار مساحة المستخدم هو السماح بإجراء استدعاءات النظام نيابة عن الهدف. ينبغي إما أن يتولى المشرف معالجة استدعاء نظام الهدف أو يُسمح له بالاستمرار بشكل طبيعي في النواة (حيث ستُطبق سياسات الأمان القياسية). .P \fBملاحظة هامة\fP: يجب عدم استخدام هذه الآلية لاتخاذ قرارات سياسة الأمان بشأن استدعاء النظام، وهو ما سيكون عرضة لسباق البيانات بطبيعته لأسباب موصوفة لاحقًا. .P يجب استخدام العلم \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP بحذر. إذا ضبطه المشرف، فسيستمر استدعاء نظام الهدف. ومع ذلك، يوجد سباق "وقت التحقق مقابل وقت الاستخدام" هنا، حيث يمكن لمهاجم استغلال الفاصل الزمني الذي يكون فيه الهدف محظوراً بانتظار استجابة "الاستمرار" للقيام بأمور مثل إعادة كتابة وسائط استدعاء النظام. .P لاحظ علاوة على ذلك أنه يمكن تجاوز مُخطِر مساحة المستخدم إذا سمحت المرشحات الحالية باستخدام \fBseccomp\fP(2) أو \fBprctl\fP(2) لتثبيت مرشح يعيد قيمة إجراء ذات أسبقية أعلى من \fBSECCOMP_RET_USER_NOTIF\fP (راجع \fBseccomp\fP(2)). .P .\" لذا ينبغي أن يكون واضحاً تماماً أن آلية إشعار مساحة مستخدم seccomp \fBلا يمكن\fP استخدامها لتنفيذ سياسة أمان! يجب استخدامها فقط في السيناريوهات التي تتوفر فيها عملية أكثر امتيازاً تشرف على استدعاءات نظام هدف أقل امتيازاً للالتفاف على قيود الأمان التي تفرضها النواة عندما يرى المشرف أن هذا آمن. وبعبارة أخرى، من أجل مواصلة استدعاء نظام، يجب أن يتأكد المشرف من أن آلية أمان أخرى أو النواة نفسها ستحظر استدعاء النظام بشكل كافٍ إذا أُعيدت كتابة وسائطه إلى شيء غير آمن. .SS "تحذيرات بشأن استخدام" أشار النقاش أعلاه في \fI/proc/\fPtid\fI/mem\fP إلى الحاجة لاستخدام \fBioctl\fP(2) \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP عند فتح ملف \fI/proc/\fPtid\fI/mem\fP الخاص بالهدف لتجنب إمكانية الوصول إلى ذاكرة العملية الخاطئة في حال انتهاء الهدف وإعادة تدوير معرفه بواسطة خيط آخر (غير ذي صلة). ومع ذلك، فإن استخدام عملية \fBioctl\fP(2) هذه ضروري أيضًا في مواقف أخرى، كما هو موضح في الفقرات التالية. .P تأمل السيناريو التالي، حيث يحاول المشرف قراءة وسيط مسار استدعاء نظام \fBmount\fP(2) المحظور للهدف: .IP (1) 5 من إحدى وظائفه (\fIfunc()\fP)، يستدعي الهدف \fBmount\fP(2)، مما يطلق إشعار مساحة مستخدم ويتسبب في حظر الهدف. .IP (2) يتلقى المشرف الإشعار، ويفتح \fI/proc/\fPtid\fI/mem\fP، ويجري فحص \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP (بنجاح). .IP (3) يتلقى الهدف إشارة، مما يتسبب في إجهاض \fBmount\fP(2). .IP (4) يُنفذ معالج الإشارة في الهدف، ثم يعود. .IP (5) عند العودة من المعالج، يُستأنف تنفيذ \fIfunc()\fP)، وتعود (وربما تُستدعى وظائف أخرى، مما يعيد الكتابة فوق الذاكرة التي كانت تُستخدم لإطار مكدس \fIfunc()\fP). .IP (6) باستخدام العنوان الموفر في معلومات الإشعار، يقرأ المشرف من موقع ذاكرة الهدف الذي كان يحتوي على المسار. .IP (7) يستدعي المشرف الآن \fBmount\fP(2) مع بعض البايتات العشوائية التي حُصل عليها في الخطوة السابقة. .P الاستنتاج من السيناريو أعلاه هو كالتالي: بما أن استدعاء نظام الهدف المحظور قد يقاطعه معالج إشارة، يجب كتابة المشرف ليتوقع أن الهدف قد يتخلى عن استدعاء النظام الخاص به في \fBأي\fP وقت؛ وفي مثل هذه الحالة، يجب اعتبار أي معلومات حصل عليها المشرف من ذاكرة الهدف غير صالحة. .P لمنع مثل هذه السيناريوهات، يجب فصل كل عملية قراءة من ذاكرة الهدف عن استخدام البايتات التي حُصل عليها بفحص \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP. وفي المثال أعلاه، سيوضع الفحص بين الخطوتين الأخيرتين. يظهر مثال على هذا الفحص في قسم "أمثلة". .P .\" بناءً على ما سبق، ينبغي أن يكون واضحاً أن الكتابة بواسطة المشرف في ذاكرة الهدف لا يمكن \fBأبداً\fP اعتبارها آمنة. .SS "تحذيرات بشأن حظر استدعاءات النظام" افترض أن الهدف يجري استدعاء نظام حاصراً (مثل \fBaccept\fP(2)) ينبغي أن يعالجه المشرف. قد ينفذ المشرف بدوره نفس استدعاء النظام الحاصر. .P في هذا السيناريو، من المهم ملاحظة أنه إذا قوطع استدعاء نظام الهدف الآن بواسطة إشارة، فإن المشرف \fIلا\fP يُخطر بذلك. وإذا لم يتخذ المشرف خطوات مناسبة لاكتشاف إلغاء استدعاء نظام الهدف بنشاط، فقد تحدث صعوبات مختلفة. وبالأخذ بمثال \fBaccept\fP(2)، قد يظل المشرف محظوراً في \fBaccept\fP(2) الخاص به ممسكاً برقم منفذ قد يتوقع الهدف (الذي ربما أغلق مقبس الاستماع الخاص به بعد مقاطعته من معالج الإشارة) أن يتمكن من إعادة استخدامه في استدعاء \fBbind\fP(2). .P لذلك، عندما يرغب المشرف في محاكاة استدعاء نظام حاصر، يجب أن يفعل ذلك بطريقة تجعله يُخطر إذا قوطع استدعاء نظام الهدف بواسطة معالج إشارة. على سبيل المثال، إذا نفذ المشرف نفسه نفس استدعاء النظام الحاصر، فيمكنه حينئذٍ توظيف خيط منفصل يستخدم عملية \fBSECCOMP_IOCTL_NOTIF_ID_VALID\fP للتحقق مما إذا كان الهدف لا يزال محظوراً في استدعاء النظام الخاص به. وبدلاً من ذلك، في مثال \fBaccept\fP(2)، قد يستخدم المشرف \fBpoll\fP(2) لمراقبة كل من واصف ملف الإشعار (لاكتشاف وقت مقاطعة استدعاء \fBaccept\fP(2) للهدف) وواصف ملف الاستماع (لمعرفة وقت توفر اتصال). .P .\" إذا قوطع استدعاء نظام الهدف، يجب على المشرف توخي الحذر لتحرير الموارد (مثل واصفات الملفات) التي حصل عليها نيابة عن الهدف. .SS "التفاعل مع معالجات إشارة SA_RESTART" تأمل السيناريو التالي: .IP (1) 5 استخدمت العملية الهدف \fBsigaction\fP(2) لتثبيت معالج إشارة مع العلم \fBSA_RESTART\fP. .IP (2) أجرى الهدف استدعاء نظام أطلق إشعار مساحة مستخدم seccomp، والهدف محظور حالياً حتى يرسل المشرف استجابة إشعار. .IP (3) سُلّمت إشارة إلى الهدف ونُفذ معالج الإشارة. .IP (4) عندما (إذا) حاول المشرف إرسال استجابة إشعار، ستفشل عملية \fBioctl\fP(2) \fBSECCOMP_IOCTL_NOTIF_SEND\fP مع الخطأ \fBENOENT\fP. .P في هذا السيناريو، ستعيد النواة تشغيل استدعاء نظام الهدف. وبناءً على ذلك، سيتلقى المشرف إشعاراً آخر في مساحة المستخدم. وهكذا، اعتماداً على عدد مرات مقاطعة استدعاء النظام المحظور بواسطة معالج إشارة، قد يتلقى المشرف إشعارات متعددة لنفس نسخة استدعاء النظام في الهدف. .P .\" FIXME .\" About the above, Kees Cook commented: .\" .\" Does this need fixing? I imagine the correct behavior for this case .\" would be a response to _SEND of EINPROGRESS and the target would see .\" EINTR normally? .\" .\" I mean, it's not like seccomp doesn't already expose weirdness with .\" syscall restarts. Not even arm64 compat agrees[3] with arm32 in this .\" regard. :( من الأمور الغريبة أن إعادة تشغيل استدعاء النظام كما هو موضح في هذا السيناريو ستحدث حتى بالنسبة لاستدعاءات النظام الحاصرة المدرجة في \fBsignal\fP(7) والتي لا يُعاد تشغيلها \fBأبداً\fP في العادة بواسطة العلم \fBSA_RESTART\fP. . .\" FIXME .\" Michael Kerrisk: .\" I wonder about the effect of this oddity for system calls that .\" are normally nonrestartable because they have timeouts. My .\" understanding is that the kernel doesn't restart those system .\" calls because it's impossible for the kernel to restart the call .\" with the right timeout value. I wonder what happens when those .\" system calls are restarted in the scenario we're discussing.) .P علاوة على ذلك، إذا كانت استجابة المشرف هي واصف ملف أُضيف باستخدام \fBSECCOMP_IOCTL_NOTIF_ADDFD\fP، فيمكن حينئذٍ استخدام العلم \fBSECCOMP_ADDFD_FLAG_SEND\fP لإضافة واصف الملف وإعادة تلك القيمة ذرياً، مما يضمن عدم تسرب أي واصفات ملفات عن غير قصد إلى الهدف. .SH العلل .\" or a poll/epoll/select .\" FIXME .\" Comment from Kees Cook: .\" .\" I want this fixed. It caused me no end of pain when building the .\" selftests, and ended up spawning my implementing a global test timeout .\" in kselftest. :P Before the usage counter refactor, there was no sane .\" way to deal with this, but now I think we're close. .\" إذا أُجريت عملية \fBSECCOMP_IOCTL_NOTIF_RECV\fP \fBioctl\fP(2) بعد انتهاء الهدف، فإن استدعاء \fBioctl\fP(2) يحظر ببساطة (بدلاً من إعادة خطأ يشير إلى أن الهدف لم يعد موجوداً). .SH أمثلة يوضح البرنامج الموضح أدناه (المصمم لغرض العرض) استخدام الواجهات الموصوفة في هذه الصفحة. ينشئ البرنامج عملية ابنة تعمل كعملية "هدف". تثبت العملية الابنة مرشح seccomp يعيد قيمة إجراء \fBSECCOMP_RET_USER_NOTIF\fP إذا أُجري استدعاء لـ \fBmkdir\fP(2). ثم تستدعي العملية الابنة \fBmkdir\fP(2) مرة واحدة لكل وسيط من وسائط سطر الأوامر الموفرة، وتبلغ عن النتيجة التي أعادها الاستدعاء. وبعد معالجة كافة الوسائط، تنتهي العملية الابنة. .P تعمل العملية الأب كمشرف، حيث تستمع للإشعارات التي تُولّد عندما تستدعي العملية الهدف \fBmkdir\fP(2). وعند حدوث مثل هذا الإشعار، يفحص المشرف ذاكرة العملية الهدف (باستخدام \fI/proc/\fPpid\fI/mem\fP) لاكتشاف وسيط المسار الذي وُفر لاستدعاء \fBmkdir\fP(2)، وينفذ أحد الإجراءات التالية: .IP \[bu] 3 إذا بدأ المسار بالبادئة "/tmp/"، يحاول المشرف إنشاء الدليل المحدد، ثم يزيف عودة للعملية الهدف بناءً على القيمة المعادة من استدعاء \fBmkdir\fP(2) الخاص بالمشرف. وفي حال نجاح ذلك الاستدعاء، تكون قيمة العودة الناجحة المزيفة هي طول المسار. .IP \[bu] إذا بدأ المسار بـ "./" (أي أنه مسار نسبي)، يرسل المشرف استجابة \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP إلى النواة للإشارة إلى أنه يجب على النواة تنفيذ استدعاء \fBmkdir\fP(2) للعملية الهدف. .IP \[bu] إذا بدأ المسار ببادئة أخرى، يزيف المشرف عودة خطأ للعملية الهدف، لكي يبدو أن استدعاء \fBmkdir\fP(2) للعملية الهدف قد فشل مع الخطأ \fBEOPNOTSUPP\fP ("العملية غير مدعومة"). بالإضافة إلى ذلك، إذا كان المسار المحدد هو "/bye" بالضبط، فينتهي المشرف. .P يمكن استخدام هذا البرنامج لتوضيح جوانب مختلفة من سلوك آلية إشعار مساحة مستخدم seccomp. وللمساعدة في مثل هذه العروض التوضيحية، يسجل البرنامج رسائل متنوعة لإظهار عملية العملية الهدف (السطور المسبوقة بـ "T:") والمشرف (السطور المزاحة والمسبوقة بـ "S:"). .P في المثال التالي، يحاول الهدف إنشاء الدليل \fI/tmp/x\fP. وعند تلقي الإشعار، ينشئ المشرف الدليل نيابة عن الهدف، ويزيف عودة ناجحة ليتلقاها استدعاء \fBmkdir\fP(2) للعملية الهدف. .P .in +4n .EX $\fB ./seccomp_unotify /tmp/x\fP; T: PID = 23168 \& T: على وشك إجراء mkdir("/tmp/x") S: جُلب إشعار (ID 0x17445c4a0f4e0e3c) لـ PID 23168 S: جارِ تنفيذ: mkdir("/tmp/x", 0700) S: نجاح! عودة مزيفة = 6 S: جارِ إرسال استجابة (flags = 0; val = 6; error = 0) T: نجاح: mkdir(2) أعاد 6 \& T: جارِ الإنهاء S: انتهى الهدف؛ وداعاً .EE .in .P في المخرجات أعلاه، لاحظ أن قيمة العودة المزيفة التي تراها العملية الهدف هي 6 (طول المسار \fI/tmp/x\fP)، بينما يعيد استدعاء \fBmkdir\fP(2) العادي 0 عند النجاح. .P في المثال التالي، يحاول الهدف إنشاء دليل باستخدام المسار النسبي \&\fI./sub\fP. وبما أن هذا المسار يبدأ بـ "./"، يرسل المشرف استجابة \fBSECCOMP_USER_NOTIF_FLAG_CONTINUE\fP إلى النواة، وتنفذ النواة حينئذٍ (بنجاح) استدعاء \fBmkdir\fP(2) للعملية الهدف. .P .in +4n .EX $\fB ./seccomp_unotify ./sub\fP; T: PID = 23204 \& T: على وشك إجراء mkdir("./sub") S: جُلب إشعار (ID 0xddb16abe25b4c12) لـ PID 23204 S: يمكن للهدف تنفيذ استدعاء النظام S: جارِ إرسال استجابة (flags = 0x1; val = 0; error = 0) T: نجاح: mkdir(2) أعاد 0 \& T: جارِ الإنهاء S: انتهى الهدف؛ وداعاً .EE .in .P إذا حاولت العملية الهدف إنشاء دليل بمسار لا يبدأ بـ "." ولا يبدأ بالبادئة "/tmp/"، فإن المشرف يزيف عودة خطأ (\fBEOPNOTSUPP\fP، "العملية غير مدعومة") لاستدعاء \fBmkdir\fP(2) للهدف (الذي لا يُنفذ): .P .in +4n .EX $\fB ./seccomp_unotify /xxx\fP; T: PID = 23178 \& T: على وشك إجراء mkdir("/xxx") S: جُلب إشعار (ID 0xe7dc095d1c524e80) لـ PID 23178 S: جارِ تزييف استجابة خطأ (العملية غير مدعومة) S: جارِ إرسال استجابة (flags = 0; val = 0; error = \-95) T: خطأ: mkdir(2): العملية غير مدعومة \& T: جارِ الإنهاء S: انتهى الهدف؛ وداعاً .EE .in .P في المثال التالي، تحاول العملية الهدف إنشاء دليل بالمسار \fB/tmp/nosuchdir/b\fP. وعند تلقي الإشعار، يحاول المشرف إنشاء ذلك الدليل، ولكن يفشل استدعاء \fBmkdir\fP(2) لأن الدليل \fB/tmp/nosuchdir\fP غير موجود. وبناءً على ذلك، يزيف المشرف عودة خطأ تمرر الخطأ الذي تلقاه مرة أخرى إلى استدعاء \fBmkdir\fP(2) للعملية الهدف. .P .in +4n .EX $\fB ./seccomp_unotify /tmp/nosuchdir/b\fP; T: PID = 23199 \& T: على وشك إجراء mkdir("/tmp/nosuchdir/b") S: جُلب إشعار (ID 0x8744454293506046) لـ PID 23199 S: جارِ تنفيذ: mkdir("/tmp/nosuchdir/b", 0700) S: فشل! (errno = 2; لا يوجد ملف أو دليل كهذا) S: جارِ إرسال استجابة (flags = 0; val = 0; error = \-2) T: خطأ: mkdir(2): لا يوجد ملف أو دليل كهذا \& T: جارِ الإنهاء S: انتهى الهدف؛ وداعاً .EE .in .P إذا تلقى المشرف إشعارًا ورأى أن وسيط \fBmkdir\fP(2) الخاص بالهدف هو السلسلة "/bye"، فحينئذٍ ينهي المشرف عمله (بالإضافة إلى تزييف خطأ \fBEOPNOTSUPP\fP). وإذا نفذت عملية الهدف لاحقًا \fBmkdir\fP(2) أخرى تحفز مرشح seccomp الخاص بها ليعيد قيمة الإجراء \fBSECCOMP_RET_USER_NOTIF\fP، فتتسبب النواة في فشل استدعاء النظام لعملية الهدف بخطأ \fBENOSYS\fP ("الوظيفة غير مفعلة"). ويُوضح ذلك في المثال التالي: .P .in +4n .EX $\fB ./seccomp_unotify /bye /tmp/y\fP; T: PID = 23185 \& T: على وشك إجراء mkdir("/bye") S: جُلب إشعار (ID 0xa81236b1d2f7b0f4) لـ PID 23185 S: جارِ تزييف استجابة خطأ (العملية غير مدعومة) S: جارِ إرسال استجابة (flags = 0; val = 0; error = \-95) S: جارِ الإنهاء ********** T: خطأ: mkdir(2): العملية غير مدعومة \& T: على وشك إجراء mkdir("/tmp/y") T: خطأ: mkdir(2): الوظيفة غير منفذة \& T: جارِ الإنهاء .EE .in .\" .SS "مصدر البرنامج" .\" SRC BEGIN (seccomp_unotify.c) .EX #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include \& #define streq(...) (strcmp(__VA_ARGS__) == 0) \& /* Send the file descriptor \[aq]fd\[aq] over the connected UNIX domain socket \[aq]sockfd\[aq]. Returns 0 on success, or \-1 on error. */ \& static int sendfd(int sockfd, int fd) { int data; struct iovec iov; struct msghdr msgh; struct cmsghdr *cmsgp; \& /* Allocate a char array of suitable size to hold the ancillary data. However, since this buffer is in reality a \[aq]struct cmsghdr\[aq], use a union to ensure that it is suitably aligned. */ union { char buf[CMSG_SPACE(sizeof(int))]; /* Space large enough to hold an \[aq]int\[aq] */ struct cmsghdr align; } controlMsg; \& /* The \[aq]msg_name\[aq] field can be used to specify the address of the destination socket when sending a datagram. However, we do not need to use this field because \[aq]sockfd\[aq] is a connected socket. */ \& msgh.msg_name = NULL; msgh.msg_namelen = 0; \& /* On Linux, we must transmit at least one byte of real data in order to send ancillary data. We transmit an arbitrary integer whose value is ignored by recvfd(). */ \& msgh.msg_iov = &iov; msgh.msg_iovlen = 1; iov.iov_base = &data; iov.iov_len = sizeof(int); data = 12345; \& /* Set \[aq]msghdr\[aq] fields that describe ancillary data */ \& msgh.msg_control = controlMsg.buf; msgh.msg_controllen = sizeof(controlMsg.buf); \& /* Set up ancillary data describing file descriptor to send */ \& cmsgp = CMSG_FIRSTHDR(&msgh); cmsgp\->cmsg_level = SOL_SOCKET; cmsgp\->cmsg_type = SCM_RIGHTS; cmsgp\->cmsg_len = CMSG_LEN(sizeof(int)); memcpy(CMSG_DATA(cmsgp), &fd, sizeof(int)); \& /* Send real plus ancillary data */ \& if (sendmsg(sockfd, &msgh, 0) == \-1) return \-1; \& return 0; } \& /* Receive a file descriptor on a connected UNIX domain socket. Returns the received file descriptor on success, or \-1 on error. */ \& static int recvfd(int sockfd) { int data, fd; ssize_t nr; struct iovec iov; struct msghdr msgh; \& /* Allocate a char buffer for the ancillary data. See the comments in sendfd() */ union { char buf[CMSG_SPACE(sizeof(int))]; struct cmsghdr align; } controlMsg; struct cmsghdr *cmsgp; \& /* The \[aq]msg_name\[aq] field can be used to obtain the address of the sending socket. However, we do not need this information. */ \& msgh.msg_name = NULL; msgh.msg_namelen = 0; \& /* Specify buffer for receiving real data */ \& msgh.msg_iov = &iov; msgh.msg_iovlen = 1; iov.iov_base = &data; /* Real data is an \[aq]int\[aq] */ iov.iov_len = sizeof(int); \& /* Set \[aq]msghdr\[aq] fields that describe ancillary data */ \& msgh.msg_control = controlMsg.buf; msgh.msg_controllen = sizeof(controlMsg.buf); \& /* Receive real plus ancillary data; real data is ignored */ \& nr = recvmsg(sockfd, &msgh, 0); if (nr == \-1) return \-1; \& cmsgp = CMSG_FIRSTHDR(&msgh); \& /* Check the validity of the \[aq]cmsghdr\[aq] */ \& if (cmsgp == NULL || cmsgp\->cmsg_len != CMSG_LEN(sizeof(int)) || cmsgp\->cmsg_level != SOL_SOCKET || cmsgp\->cmsg_type != SCM_RIGHTS) { errno = EINVAL; return \-1; } \& /* Return the received file descriptor to our caller */ \& memcpy(&fd, CMSG_DATA(cmsgp), sizeof(int)); return fd; } \& static void sigchldHandler(int sig) { char msg[] = "\[rs]tS: target has terminated; bye\[rs]n"; \& write(STDOUT_FILENO, msg, sizeof(msg) \- 1); _exit(EXIT_SUCCESS); } \& static int seccomp(unsigned int operation, unsigned int flags, void *args) { return syscall(SYS_seccomp, operation, flags, args); } \& /* The following is the x86\-64\-specific BPF boilerplate code for checking that the BPF program is running on the right architecture + ABI. At completion of these instructions, the accumulator contains the system call number. */ \& /* For the x32 ABI, all system call numbers have bit 30 set */ \& #define X32_SYSCALL_BIT 0x40000000 \& #define X86_64_CHECK_ARCH_AND_LOAD_SYSCALL_NR \[rs] BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \[rs] (offsetof(struct seccomp_data, arch))), \[rs] BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 2), \[rs] BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \[rs] (offsetof(struct seccomp_data, nr))), \[rs] BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, X32_SYSCALL_BIT, 0, 1), \[rs] BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS) \& /* installNotifyFilter() installs a seccomp filter that generates user\-space notifications (SECCOMP_RET_USER_NOTIF) when the process calls mkdir(2); the filter allows all other system calls. \& The function return value is a file descriptor from which the user\-space notifications can be fetched. */ \& static int installNotifyFilter(void) { int notifyFd; \& struct sock_filter filter[] = { X86_64_CHECK_ARCH_AND_LOAD_SYSCALL_NR, \& /* mkdir() triggers notification to user\-space supervisor */ \& BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_mkdir, 0, 1), BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_USER_NOTIF), \& /* Every other system call is allowed */ \& BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), }; \& struct sock_fprog prog = { .len = countof(filter), .filter = filter, }; \& /* Install the filter with the SECCOMP_FILTER_FLAG_NEW_LISTENER flag; as a result, seccomp() returns a notification file descriptor. */ \& notifyFd = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_NEW_LISTENER, &prog); if (notifyFd == \-1) err(EXIT_FAILURE, "seccomp\-install\-notify\-filter"); \& return notifyFd; } \& /* Close a pair of sockets created by socketpair() */ \& static void closeSocketPair(int sockPair[2]) { if (close(sockPair[0]) == \-1) err(EXIT_FAILURE, "closeSocketPair\-close\-0"); if (close(sockPair[1]) == \-1) err(EXIT_FAILURE, "closeSocketPair\-close\-1"); } \& /* Implementation of the target process. Create a child process that: \& (1) installs a seccomp filter with the SECCOMP_FILTER_FLAG_NEW_LISTENER flag; (2) writes the seccomp notification file descriptor returned from the previous step onto the UNIX domain socket, \[aq]sockPair[0]\[aq]; (3) calls mkdir(2) for each element of \[aq]argv\[aq]. \& The function return value in the parent is the PID of the child process; the child does not return from this function. */ \& static pid_t targetProcess(int sockPair[2], char *argv[]) { int notifyFd, s; pid_t targetPid; \& targetPid = fork(); \& if (targetPid == \-1) err(EXIT_FAILURE, "fork"); \& if (targetPid > 0) /* In parent, return PID of child */ return targetPid; \& /* Child falls through to here */ \& printf("T: PID = %ld\[rs]n", (long) getpid()); \& /* Install seccomp filter(s) */ \& if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) err(EXIT_FAILURE, "prctl"); \& notifyFd = installNotifyFilter(); \& /* Pass the notification file descriptor to the tracing process over a UNIX domain socket */ \& if (sendfd(sockPair[0], notifyFd) == \-1) err(EXIT_FAILURE, "sendfd"); \& /* Notification and socket FDs are no longer needed in target */ \& if (close(notifyFd) == \-1) err(EXIT_FAILURE, "close\-target\-notify\-fd"); \& closeSocketPair(sockPair); \& /* Perform a mkdir() call for each of the command\-line arguments */ \& for (char **ap = argv; *ap != NULL; ap++) { printf("\[rs]nT: about to mkdir(\[rs]"%s\[rs]")\[rs]n", *ap); \& s = mkdir(*ap, 0700); if (s == \-1) perror("T: ERROR: mkdir(2)"); else printf("T: SUCCESS: mkdir(2) returned %d\[rs]n", s); } \& printf("\[rs]nT: terminating\[rs]n"); exit(EXIT_SUCCESS); } \& /* Check that the notification ID provided by a SECCOMP_IOCTL_NOTIF_RECV operation is still valid. It will no longer be valid if the target process has terminated or is no longer blocked in the system call that generated the notification (because it was interrupted by a signal). \& This operation can be used when doing such things as accessing /proc/PID files in the target process in order to avoid TOCTOU race conditions where the PID that is returned by SECCOMP_IOCTL_NOTIF_RECV terminates and is reused by another process. */ \& static bool cookieIsValid(int notifyFd, uint64_t id) { return ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ID_VALID, &id) == 0; } \& /* Access the memory of the target process in order to fetch the pathname referred to by the system call argument \[aq]argNum\[aq] in \[aq]req\->data.args[]\[aq]. The pathname is returned in \[aq]path\[aq], a buffer of \[aq]size\[aq] bytes allocated by the caller. \& Returns true if the pathname is successfully fetched, and false otherwise. For possible causes of failure, see the comments below. */ \& static bool getTargetPathname(struct seccomp_notif *req, int notifyFd, int argNum, char *path, size_t size) { int procMemFd; char procMemPath[PATH_MAX]; ssize_t nread; \& snprintf(procMemPath, sizeof(procMemPath), "/proc/%d/mem", req\->pid); \& procMemFd = open(procMemPath, O_RDONLY | O_CLOEXEC); if (procMemFd == \-1) return false; \& /* Check that the process whose info we are accessing is still alive and blocked in the system call that caused the notification. If the SECCOMP_IOCTL_NOTIF_ID_VALID operation (performed in cookieIsValid()) succeeded, we know that the /proc/PID/mem file descriptor that we opened corresponded to the process for which we received a notification. If that process subsequently terminates, then read() on that file descriptor will return 0 (EOF). */ \& if (!cookieIsValid(notifyFd, req\->id)) { close(procMemFd); return false; } \& /* Read bytes at the location containing the pathname argument */ \& nread = pread(procMemFd, path, size, req\->data.args[argNum]); \& close(procMemFd); \& if (nread <= 0) return false; \& /* Once again check that the notification ID is still valid. The case we are particularly concerned about here is that just before we fetched the pathname, the target\[aq]s blocked system call was interrupted by a signal handler, and after the handler returned, the target carried on execution (past the interrupted system call). In that case, we have no guarantees about what we are reading, since the target\[aq]s memory may have been arbitrarily changed by subsequent operations. */ \& if (!cookieIsValid(notifyFd, req\->id)) { perror("\[rs]tS: notification ID check failed!!!"); return false; } \& /* Even if the target\[aq]s system call was not interrupted by a signal, we have no guarantees about what was in the memory of the target process. (The memory may have been modified by another thread, or even by an external attacking process.) We therefore treat the buffer returned by pread() as untrusted input. The buffer should contain a terminating null byte; if not, then we will trigger an error for the target process. */ \& if (strnlen(path, nread) < nread) return true; \& return false; } \& /* Allocate buffers for the seccomp user\-space notification request and response structures. It is the caller\[aq]s responsibility to free the buffers returned via \[aq]req\[aq] and \[aq]resp\[aq]. */ \& static void allocSeccompNotifBuffers(struct seccomp_notif **req, struct seccomp_notif_resp **resp, struct seccomp_notif_sizes *sizes) { size_t resp_size; \& /* Discover the sizes of the structures that are used to receive notifications and send notification responses, and allocate buffers of those sizes. */ \& if (seccomp(SECCOMP_GET_NOTIF_SIZES, 0, sizes) == \-1) err(EXIT_FAILURE, "seccomp\-SECCOMP_GET_NOTIF_SIZES"); \& *req = malloc(sizes\->seccomp_notif); if (*req == NULL) err(EXIT_FAILURE, "malloc\-seccomp_notif"); \& /* When allocating the response buffer, we must allow for the fact that the user\-space binary may have been built with user\-space headers where \[aq]struct seccomp_notif_resp\[aq] is bigger than the response buffer expected by the (older) kernel. Therefore, we allocate a buffer that is the maximum of the two sizes. This ensures that if the supervisor places bytes into the response structure that are past the response size that the kernel expects, then the supervisor is not touching an invalid memory location. */ \& resp_size = sizes\->seccomp_notif_resp; if (sizeof(struct seccomp_notif_resp) > resp_size) resp_size = sizeof(struct seccomp_notif_resp); \& *resp = malloc(resp_size); if (*resp == NULL) err(EXIT_FAILURE, "malloc\-seccomp_notif_resp"); \& } \& /* Handle notifications that arrive via the SECCOMP_RET_USER_NOTIF file descriptor, \[aq]notifyFd\[aq]. */ \& static void handleNotifications(int notifyFd) { bool pathOK; char path[PATH_MAX]; struct seccomp_notif *req; struct seccomp_notif_resp *resp; struct seccomp_notif_sizes sizes; \& allocSeccompNotifBuffers(&req, &resp, &sizes); \& /* Loop handling notifications */ \& for (;;) { \& /* Wait for next notification, returning info in \[aq]*req\[aq] */ \& memset(req, 0, sizes.seccomp_notif); if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_RECV, req) == \-1) { if (errno == EINTR) continue; err(EXIT_FAILURE, "\[rs]tS: ioctl\-SECCOMP_IOCTL_NOTIF_RECV"); } \& printf("\[rs]tS: got notification (ID %#llx) for PID %d\[rs]n", req\->id, req\->pid); \& /* The only system call that can generate a notification event is mkdir(2). Nevertheless, we check that the notified system call is indeed mkdir() as kind of future\-proofing of this code in case the seccomp filter is later modified to generate notifications for other system calls. */ \& if (req\->data.nr != SYS_mkdir) { printf("\[rs]tS: notification contained unexpected " "system call number; bye!!!\[rs]n"); exit(EXIT_FAILURE); } \& pathOK = getTargetPathname(req, notifyFd, 0, path, sizeof(path)); \& /* Prepopulate some fields of the response */ \& resp\->id = req\->id; /* Response includes notification ID */ resp\->flags = 0; resp\->val = 0; \& /* If getTargetPathname() failed, trigger an EINVAL error response (sending this response may yield an error if the failure occurred because the notification ID was no longer valid); if the directory is in /tmp, then create it on behalf of the supervisor; if the pathname starts with \[aq].\[aq], tell the kernel to let the target process execute the mkdir(); otherwise, give an error for a directory pathname in any other location. */ \& if (!pathOK) { resp\->error = \-EINVAL; printf("\[rs]tS: spoofing error for invalid pathname (%s)\[rs]n", strerror(\-resp\->error)); } else if (strncmp(path, "/tmp/", strlen("/tmp/")) == 0) { printf("\[rs]tS: executing: mkdir(\[rs]"%s\[rs]", %#llo)\[rs]n", path, req\->data.args[1]); \& if (mkdir(path, req\->data.args[1]) == 0) { resp\->error = 0; /* "Success" */ resp\->val = strlen(path); /* Used as return value of mkdir() in target */ printf("\[rs]tS: success! spoofed return = %lld\[rs]n", resp\->val); } else { \& /* If mkdir() failed in the supervisor, pass the error back to the target */ \& resp\->error = \-errno; printf("\[rs]tS: failure! (errno = %d; %s)\[rs]n", errno, strerror(errno)); } } else if (strncmp(path, "./", strlen("./")) == 0) { resp\->error = resp\->val = 0; resp\->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; printf("\[rs]tS: target can execute system call\[rs]n"); } else { resp\->error = \-EOPNOTSUPP; printf("\[rs]tS: spoofing error response (%s)\[rs]n", strerror(\-resp\->error)); } \& /* Send a response to the notification */ \& printf("\[rs]tS: sending response " "(flags = %#x; val = %lld; error = %d)\[rs]n", resp\->flags, resp\->val, resp\->error); \& if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_SEND, resp) == \-1) { if (errno == ENOENT) printf("\[rs]tS: response failed with ENOENT; " "perhaps target process\[aq]s syscall was " "interrupted by a signal?\[rs]n"); else perror("ioctl\-SECCOMP_IOCTL_NOTIF_SEND"); } \& /* If the pathname is just "/bye", then the supervisor breaks out of the loop and terminates. This allows us to see what happens if the target process makes further calls to mkdir(2). */ \& if (streq(path, "/bye")) break; } \& free(req); free(resp); printf("\[rs]tS: terminating **********\[rs]n"); exit(EXIT_FAILURE); } \& /* Implementation of the supervisor process: \& (1) obtains the notification file descriptor from \[aq]sockPair[1]\[aq] (2) handles notifications that arrive on that file descriptor. */ \& static void supervisor(int sockPair[2]) { int notifyFd; \& notifyFd = recvfd(sockPair[1]); \& if (notifyFd == \-1) err(EXIT_FAILURE, "recvfd"); \& closeSocketPair(sockPair); /* We no longer need the socket pair */ \& handleNotifications(notifyFd); } \& int main(int argc, char *argv[]) { int sockPair[2]; struct sigaction sa; \& setbuf(stdout, NULL); \& if (argc < 2) { fprintf(stderr, "At least one pathname argument is required\[rs]n"); exit(EXIT_FAILURE); } \& /* Create a UNIX domain socket that is used to pass the seccomp notification file descriptor from the target process to the supervisor process. */ \& if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockPair) == \-1) err(EXIT_FAILURE, "socketpair"); \& /* Create a child process\-\-the "target"\-\-that installs seccomp filtering. The target process writes the seccomp notification file descriptor onto \[aq]sockPair[0]\[aq] and then calls mkdir(2) for each directory in the command\-line arguments. */ \& (void) targetProcess(sockPair, &argv[optind]); \& /* Catch SIGCHLD when the target terminates, so that the supervisor can also terminate. */ \& sa.sa_handler = sigchldHandler; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); if (sigaction(SIGCHLD, &sa, NULL) == \-1) err(EXIT_FAILURE, "sigaction"); \& supervisor(sockPair); \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SH "انظر أيضًا" \fBioctl\fP(2)، \fBpidfd_getfd\fP(2)، \fBpidfd_open\fP(2)، \fBseccomp\fP(2) .P يمكن العثور على مزيد من الأمثلة في ملف مصدر النواة \fIsamples/seccomp/user\-trap.c\fP. .PP .SH ترجمة تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي . .PP هذه الترجمة هي وثيقة مجانية؛ راجع .UR https://www.gnu.org/licenses/gpl-3.0.html رخصة جنو العامة الإصدار 3 .UE أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات. .PP إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: .MT kde-l10n-ar@kde.org .ME .