WASI Standard: تشغيل WebAssembly خارج الويب

في 27 مارس ، أعلنا في Mozilla بدء توحيد WASI ، واجهة نظام WebAssembly (واجهة نظام WebAssembly).

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

ما: WebAssembly هي أداة تجميع لآلية مفاهيمية بدلاً من آلة فعلية. يعمل على العديد من البنى ، لذلك ، هناك حاجة إلى واجهة نظام لنظام تشغيل مفاهيمي للعمل على أنظمة تشغيل مختلفة.

إليك ما هي واجهة WASI: إنها واجهة نظام لمنصة WebAssembly.

نحن نسعى جاهدين لإنشاء واجهة نظام ستصبح رفيقًا حقيقيًا لـ WebAssembly مع أقصى درجات الحماية والأمان.

من: كجزء من فريق تطوير WebAssembly ، قمنا بتنظيم مجموعة فرعية من شأنها توحيد معايير WASI . لقد جمعنا بالفعل شركاء مهتمين ونبحث عن شركاء جدد.

فيما يلي بعض الأسباب التي تجعلنا نحن وشركائنا ومؤيدينا نعتبر هذا الأمر مهمًا:

شون وايت ، مدير البحث والتطوير في موزيلا:
"يقوم WebAssembly بالفعل بتغيير الطريقة التي يقدم بها الأشخاص أنواعًا جديدة من المحتوى الجذاب. إنه يساعد مطوري المحتوى ومنشئي المحتوى. لقد نجح كل شيء حتى الآن من خلال المتصفحات ، ولكن مع WASI ، سيستفيد WebAssembly المزيد من المستخدمين والمزيد من الأجهزة في أماكن مختلفة. "

تايلر مكملن
"نرى WebAssembly كمنصة لتنفيذ التعليمات البرمجية بسرعة وأمان بأمان على سحابة حافة. على الرغم من البيئات المختلفة (الحافة والمتصفحات) ، فبفضل WASI ، لا يتعين عليك نقل الكود إلى كل منصة. "

مايلز بورينس ، المدير التنفيذي للجنة Node التوجيهية:
"يمكن لـ WebAssembly حل إحدى أكبر مشاكل Node: كيفية تحقيق قرب السرعة الأصلية وإعادة استخدام التعليمات البرمجية المكتوبة بلغات أخرى مثل C و C ++ ، مع الحفاظ على قابلية النقل والأمان. توحيد WASI هو الخطوة الأولى نحو هذا ".

لوري فوس ، المؤسس المشارك لـ npm:
"Npm متحمس للغاية حول WebAssembly المحتمل لنظام npm البيئي ، لأنه يجعل من الأسهل بكثير تشغيل التعليمات البرمجية الأصلية في تطبيقات JavaScript من جانب الخادم. ونحن نتطلع إلى نتائج هذه العملية. "

لذلك هذا هو الحدث الكبير!

يوجد حاليًا ثلاثة تطبيقات WASI:

  • وقت التشغيل ، وقت تشغيل Mozilla WebAssembly
  • وسيت ، فاستلي WebAssembly وقت التشغيل
  • متصفح polyfill

مظاهرة WASI في العمل:


بعد ذلك ، سنتحدث عن اقتراح موزيلا حول كيفية عمل واجهة النظام هذه.

ما هي واجهة النظام؟


يقول العديد من اللغات مثل C توفر الوصول المباشر إلى موارد النظام. ولكن هذا ليس صحيحا تماما. في معظم الأنظمة ، لا تتمتع هذه اللغات بوصول مباشر إلى أشياء مثل فتح الملفات أو إنشائها. لم لا؟

لأن موارد النظام هذه - الملفات والذاكرة واتصالات الشبكة - مهمة جدًا لتحقيق الاستقرار والأمان.

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



لذلك ، تحتاج إلى طريقة للتحكم في البرامج والمستخدمين الذين يمكنهم الوصول إلى الموارد. لفترة طويلة ، توصل مطورو النظام إلى طريقة لتوفير مثل هذا التحكم: حلقات الحماية.

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

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



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

في معظم الأجهزة ، الطريقة الوحيدة للوصول إلى موارد النظام هي من خلال مكالمات النظام.



يوفر نظام التشغيل إمكانية الوصول إلى مكالمات النظام. ولكن إذا كان لكل نظام تشغيل مكالمات نظامه الخاص به ، ألا يحتاجون إلى كتابة إصدارات مختلفة من الكود؟ لحسن الحظ لا. يتم حل المشكلة باستخدام التجريد.

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

هذا هو المكان الذي يظهر فيه مفهوم واجهة النظام. على سبيل المثال ، إذا قمت بترجمة printf لجهاز Windows ، فسيستخدم واجهة برمجة تطبيقات Windows. إذا تم تجميعه لنظام التشغيل Mac أو Linux ، فإنه يستخدم POSIX.



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



لقد سبق أن قلت إن WebAssembly هي أداة تجميع لآلة مفاهيمية وليست آلة حقيقية. وبالمثل ، يحتاج WebAssembly إلى واجهة نظام لنظام تشغيل افتراضي وليس نظام تشغيل حقيقي.

ولكن هناك بالفعل أوقات تشغيل يمكنها تشغيل WebAssembly خارج المستعرض ، حتى بدون واجهة النظام هذه. كيف يفعلون ذلك؟ لنرى.

كيف يعمل WebAssembly الآن خارج المستعرض؟


كانت الأداة الأولى لإنشاء رمز WebAssembly هي Emscripten. إنها تحاكي على شبكة الإنترنت واجهة نظام تشغيل محددة - POSIX. هذا يعني أنه يمكن للمبرمج استخدام الوظائف من مكتبة C القياسية (libc).

لهذا ، يستخدم Emscripten تطبيق libc الخاص به. وهي مقسمة إلى جزأين: الأول يتم تجميعه في وحدة WebAssembly ، ويتم تنفيذ الآخر في كود JS-glue. يرسل غراء JS هذا المكالمات إلى المتصفح الذي يتحدث إلى نظام التشغيل.



يتم ترجمة معظم رمز WebAssembly المبكر مع Emscripten. لذلك ، عندما بدأ الناس يريدون تشغيل WebAssembly دون مستعرض ، بدأوا في تشغيل رمز Emscripten.

لذا في أوقات التشغيل هذه ، يجب عليك إنشاء تطبيقاتك الخاصة لجميع الوظائف التي كانت موجودة في كود JS-glue.

ولكن هناك مشكلة. لم يتم تصميم الواجهة التي يوفرها رمز الغراء JS كواجهة قياسية أو حتى عامة. على سبيل المثال ، للاتصال مثل read في واجهة برمجة التطبيقات (API) العادية ، يستخدم رمز الغراء JS _system3(which, varargs) النظام _system3(which, varargs) .



المعلمة الأولى which هي عدد صحيح يطابق دائماً الرقم في الاسم (في حالتنا 3).

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

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

هذا يعني أنهم يعيدون تنفيذ التعليمات البرمجية (على سبيل المثال ، تمرير الوسائط كقيم كومة الذاكرة المؤقتة) ، وهو الأمر المنطقي بالنظر إلى قيود Emscripten ، ولكن لا توجد قيود من هذا القبيل في بيئات وقت التشغيل هذه.



إذا كنا نبني نظام WebAssembly البيئي لعقود مقبلة ، فهو يحتاج إلى أساس متين وليس عكازين. هذا يعني أن معيارنا الفعلي لا يمكن أن يكون مضاهاة مضاهاة.

ولكن ما هي المبادئ التي تنطبق في هذه الحالة؟

ما هي المبادئ التي يجب على واجهة نظام WebAssembly الالتزام بها؟


مبدأان أساسيان في WebAssembly:

  • قابلية
  • سلامة

نحن نتجاوز المتصفح ، ولكننا نحتفظ بهذه المبادئ الأساسية.

ومع ذلك ، فإن أسلوب POSIX ونظام التحكم في الوصول إلى Unix لا يعطينا النتيجة المرجوة. دعونا نرى ما هي المشكلة.

قابلية


يوفر POSIX إمكانية نقل شفرة المصدر. يمكنك تجميع نفس شفرة المصدر مع إصدارات مختلفة من libc لأجهزة كمبيوتر مختلفة.



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



هذا يبسط توزيع التعليمات البرمجية.

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

سلامة


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

على سبيل المثال ، يطلب برنامج فتح ملف. المستخدم لديه مجموعة محددة من الملفات التي لديه حق الوصول إليها.

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



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

كل شيء قد تغير. حاليًا ، عادة ما تكون الأنظمة مستخدمًا فرديًا ، ولكنها تستخدم كود جهة خارجية موثوقية غير معروفة. الآن يأتي التهديد الرئيسي من الكود الذي تديره بنفسك.

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


تطبيق مشبوه : أنا أعمل للمستخدم بوب. هل يمكنني فتح محفظة بيتكوين الخاصة به؟
كور : لبوب؟ بالطبع!
التطبيق المشبوهة : عظيم! ماذا عن اتصال الشبكة؟

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

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

وجود صندوق حماية في حد ذاته لا يجعل النظام آمنًا - لا يزال بإمكان المضيف نقل الوظيفة الكاملة إلى صندوق الحماية ، وفي هذه الحالة لا يوفر أي حماية. لكن الصندوق الرملي يوفر على الأقل فرصة نظرية للمضيفين لبناء نظام أكثر أمانًا.


WA : من فضلك ، إليك بعض الألعاب الآمنة للتفاعل مع نظام التشغيل (safe_write ، safe_read).
تطبيق مشبوه : يا لعنة ... أين هو وصولي إلى الشبكة؟

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

ما ينبغي أن تبدو مثل واجهة النظام؟


بالنظر إلى هذين المبدأين الرئيسيين ، ماذا يجب أن تكون واجهة نظام WebAssembly؟

هذا سنكتشف في عملية التوحيد. ومع ذلك ، لدينا اقتراح للبدء:

  • إنشاء مجموعة وحدات من واجهات القياسية
  • لنبدأ بتوحيد الوحدة الأساسية لـ wasi-core.




ماذا سيكون في wasi-core؟ هذه هي الأساسيات التي تحتاجها جميع البرامج. ستغطي الوحدة معظم وظائف POSIX ، بما في ذلك الملفات واتصالات الشبكة والساعات والأرقام العشوائية.

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

لكن wasi-core لا يغطي جميع وظائف POSIX. على سبيل المثال ، لا يتوافق مفهوم العملية بوضوح مع WebAssembly. بالإضافة إلى ذلك ، من الواضح أن كل مشغل WebAssembly يجب أن يدعم عمليات التشغيل مثل fork . لكننا نريد أيضًا جعل توحيد fork ممكنًا.



لغات مثل Rust سوف تستخدم wasi-core مباشرة في مكتباتهم القياسية. على سبيل المثال ، يتم تطبيق open from Rust عند التحويل البرمجي إلى WebAssembly عن طريق الاتصال بـ __wasi_path_open .

بالنسبة إلى C و C ++ ، أنشأنا wasi-sysroot ، والذي ينفذ libc من حيث وظائف wasi- الأساسية.



نتوقع أن يكون المترجمون مثل Clang قادرين على التفاعل مع واجهة برمجة تطبيقات WASI ، وسوف تستخدم سلاسل الأدوات الكاملة مثل مترجم Rust و Emscripten WASI كجزء من تطبيقات نظامهم.

كيف رمز مخصص استدعاء هذه وظائف WASI؟

يمر وقت التشغيل الذي يتم فيه تنفيذ التعليمات البرمجية بوظيفة wasi-core ، مما يضع الكائن في صندوق الحماية.



يوفر هذا إمكانية النقل ، نظرًا لأن كل مضيف يمكن أن يكون له تطبيق wasi-core خاص به على نظامه الأساسي: من أوقات تشغيل WebAssembly مثل Mozilla Wasmtime و Fastly Lucet ، إلى Node أو حتى المستعرض.

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



تقوم WASI بتعزيز الأمان وتوسيعه من خلال تقديم مفهوم أمان يستند إلى التخويل في النظام.

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

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

وبالتالي ، لا يمكن أن يكون لديك رمز يطلب منك بطريق الخطأ فتح /etc/passwd . بدلاً من ذلك ، يمكن أن تعمل التعليمات البرمجية فقط مع الدلائل الخاصة بها.



يسمح ذلك بحل مكالمات النظام المختلفة بأمان على الشفرة المعزولة لأن إمكانيات مكالمات النظام هذه محدودة.

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

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



وهذا يجعل WebAssembly أقرب إلى مبدأ الامتياز الأقل ، حيث تحصل الوحدة فقط على الوصول إلى الحد الأدنى من الموارد اللازمة للقيام بعملها.

يعتمد هذا المفهوم على الأمان القائم على الامتياز ، كما في CloudABI و Capsicum. واحدة من المشاكل مع هذه النظم هي صعوبة حمل الكود. لكننا نعتقد أن هذه المشكلة يمكن حلها.

إذا كانت الشفرة تستخدم openat بالفعل مع مسارات الملفات النسبية ، فإن ترجمة الشفرة ستعمل فقط.

إذا كانت الشفرة تستخدم الترحيل open والنمط open ، فستقدم WASI حلاً تزايديًا. باستخدام libpreopen ، تقوم بإنشاء قائمة بمسارات الملفات التي يمكن للتطبيق الوصول إليها بشكل قانوني. ثم استخدم open ، ولكن فقط مع هذه المسارات.

ما التالي؟


نعتقد أن wasi-core هي بداية جيدة. يحتفظ بقابلية وأمان WebAssembly ، مما يوفر أساسًا قويًا للنظام البيئي.

ولكن بعد التوحيد الكامل لنظام wasi-core ، هناك حاجة لحل مشكلات أخرى ، بما في ذلك:

  • غير متزامن المدخلات والمخرجات
  • مراقبة الملفات
  • قفل الملف

هذه ليست سوى البداية ، لذلك إذا كان لديك أي أفكار ، شارك !

Source: https://habr.com/ru/post/ar446764/


All Articles