From 4007ccb39da420b131b8c374f527e3ca3c3b3120 Mon Sep 17 00:00:00 2001 From: Sad Lang Dev Date: Wed, 22 Jul 2026 11:45:43 +0300 Subject: [PATCH] =?UTF-8?q?RFC:=20=D8=A5=D8=B1=D8=AE=D8=A7=D8=A1=20=D8=AD?= =?UTF-8?q?=D8=AC=D8=A8=20@=D8=B1=D9=85=D8=B2=20=D9=84=D8=AD=D8=A7=D8=B1?= =?UTF-8?q?=D8=B3=20=D8=A7=D9=84=D9=85=D9=83=D8=AF=D9=91=D8=B3=20=5F=5Fsta?= =?UTF-8?q?ck=5Fchk=5Fguard=20=D9=81=D9=8A=20=D8=A7=D9=84=D9=88=D8=B6?= =?UTF-8?q?=D8=B9=20=D8=A7=D9=84=D8=AD=D8=B1=D9=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit يوثّق قرار إرخاء حارس تصادم @رمز (SEM022) لرمز __stack_chk_guard في الوضع الحرّ حصرًا (يبقى محجوبًا في المستضاف حيث توفّره libc، ومحجوبًا __stack_chk_fail كاسم دالّة). التنفيذ مدموج في s-programming-language#239، ويُمكِّن هجرة بيانات حارس الإطار إلى ص في نواة النحلة (أخ.4). Co-Authored-By: Claude Opus 4.8 --- ...31\205\331\203\330\257\331\221\330\263.md" | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 "text/0000-\330\245\330\261\330\256\330\247\330\241-\330\255\330\254\330\250-\330\261\331\205\331\210\330\262-\330\255\330\247\330\261\330\263-\330\247\331\204\331\205\331\203\330\257\331\221\330\263.md" diff --git "a/text/0000-\330\245\330\261\330\256\330\247\330\241-\330\255\330\254\330\250-\330\261\331\205\331\210\330\262-\330\255\330\247\330\261\330\263-\330\247\331\204\331\205\331\203\330\257\331\221\330\263.md" "b/text/0000-\330\245\330\261\330\256\330\247\330\241-\330\255\330\254\330\250-\330\261\331\205\331\210\330\262-\330\255\330\247\330\261\330\263-\330\247\331\204\331\205\331\203\330\257\331\221\330\263.md" new file mode 100644 index 0000000..4a92c5d --- /dev/null +++ "b/text/0000-\330\245\330\261\330\256\330\247\330\241-\330\255\330\254\330\250-\330\261\331\205\331\210\330\262-\330\255\330\247\330\261\330\263-\330\247\331\204\331\205\331\203\330\257\331\221\330\263.md" @@ -0,0 +1,141 @@ +- **عنوان المقترح:** إرخاء حارس تصادم `@رمز` لرمز حارس المكدّس `__stack_chk_guard` في **الوضع الحرّ** حصرًا +- **النطاق:** لغة `text/` +- **تاريخ البدء:** 2026-07-22 +- **رقم الـ RFC:** 239 (= رقم PR اللغة) +- **الحالة:** مقترَح — التنفيذ مدموج في s-programming-language#239 (وثيقة قرار) +- **Issue التتبُّع:** (يُترك فارغاً حتى القبول) + +# ملخّص + +حارس تصادم رمز `@رمز` المُصدَّر (`SEM022 = SEM_SYMBOL_NAME_CONFLICT`، اللبنة +3.17) يحجب — على **المتغيّرات العامّة** — قائمةَ رموزٍ محجوزة لزمن التشغيل تضمّ +`__stack_chk_guard`. يقترح هذا المقترح **إرخاء `__stack_chk_guard` في الوضع +الحرّ فقط** (يبقى محجوبًا في المستضاف)، مع إبقاء بقيّة القائمة محجوبةً في +الوضعين (`memset`/`malloc`/`__divdi3`/`__sad_heap`…). + +الأثر المرئيّ: يستطيع برنامجٌ في الوضع الحرّ (نواة، مُقلِع) توفير رمز حارس المكدّس +الذي يطلبه `-fstack-protector` **بلغة ص** عبر `@رمز("__stack_chk_guard") متغير …`، +بدل إبقائه بيانةً بلغة C. هذا يُكمِل هجرة آليّة حارس الإطار إلى ص (كان المعالج +`__stack_chk_fail` قد هاجر في أخ.3 عبر مسار الدوالّ المنفصل). + +# الدافع (Motivation) + +حملة «إطفاء C» في نواة النحلة تهاجر كلّ ما يُعبَّر عنه بـص من طبقة التراب C إلى +ص. عند محاولة هجرة بيانات `__stack_chk_guard` (أخ.4) عبر: + +``` +@رمز("__stack_chk_guard") متغير حارس_إطار_المترجم = 3202730513 +``` + +يرفض المترجم في الوضع الحرّ: + +``` +error: (AR) @رمز("__stack_chk_guard"): اسم محجوز لزمن التشغيل المضمَّن — اختر اسمًا آخر +``` + +بينما هاجر **المعالج** `__stack_chk_fail` بنجاح في أخ.3، لأنّ `@رمز` على +**الدالّة** يمرّ بمسار توليدٍ آخر لا يستشير هذه القائمة (الخاصّة بالمتغيّرات). +فنشأ حجبٌ يمنع استعمالًا مشروعًا في الوضع الحرّ. + +## لماذا الحجب زائد في الوضع الحرّ (لا في المستضاف) + +القائمة تحرس **مصدرَي تصادم**: + +1. **رمز يُصدر المترجم له احتياطيًّا ضعيفًا** (`weak`): لكنّ إصدار هذه الرموز + محصورٌ بالوضع الحرّ (`emitFreestandingRuntime` تحت `cg_.freestanding_`). + `@رمز` عليها = تعريف ثالث ⇒ تصادم فيختار الرابط عشوائيًّا. +2. **رمز منصّة توفّره libc** في الوضع المستضاف (`memset`/`__stack_chk_guard`…): + `@رمز` قويّ عليه يقنّع رمز libc — إمّا خطأ ربط، أو **تقنيع صامت** لكوكي SSP + العشوائيّ بقيمة ثابتة ⇒ تعطيل حماية تحطّم المكدّس للعمليّة كلّها. + +تحقُّق فعليّ: `__stack_chk_guard` **غير مُصدَر** من المترجم/الرنتايم إطلاقًا +(`grep` عبر `compiler/src` و`runtime/` = صفر) ⇒ لا احتياطيّ ضعيف. إذًا في الوضع +الحرّ لا مصدر (١) ولا (٢) — `@رمز` عليه هو التعريف **الوحيد** فلا تصادم، والحجب +زائد. أمّا في المستضاف فمصدر (٢) قائم (libc) ⇒ الحجب صحيح ويبقى. + +لذا الإرخاء **مشروط بالوضع الحرّ**، لا إزالةٌ مطلقة من القائمة. + +# الشرح التوجيهي (Guide-level explanation) + +**قبل:** `__stack_chk_guard` بيانة C ثابتة في طبقة التراب: + +```c +unsigned long __stack_chk_guard = 0xBEE5CA11uL; +``` + +**بعد (وضع حرّ):** بيانة ص عبر `@رمز`، ويُبقي التراب إعلان `extern` فقط ليربطه +المولَّد: + +``` +# ص +@رمز("__stack_chk_guard") متغير حارس_إطار_المترجم = 3202730513 # 0x‏BEE5CA11 +``` +```c +/* C */ extern unsigned long __stack_chk_guard; +``` + +في المستضاف يبقى `@رمز("__stack_chk_guard")` محجوبًا (libc تملكه)، تمامًا كـ +`memset`. `__stack_chk_fail` **دالّة**: ربط **بيانة** باسمها يبقى محجوبًا في +الوضعين (نداء كائن بيانات ⇒ تعطّل)؛ ومسار `@رمز` على الدالّة منفصل غير متأثّر. + +⚠️ يجب أن يكون الحارس **`متغير`** لا `ثابت`: `ثابت` يُصدَر `constant` في +`.rodata` (للقراءة فقط)، بينما SSP يعامل `__stack_chk_guard` ككائن بيانات قابل +للكتابة (قد يُحدَّث/يُقرأ)؛ `متغير` يُصدَر `global` قابلًا للكتابة في `.data`. + +# الشرح المرجعيّ (Reference-level explanation) + +- الموضع: `compiler/src/backend/llvm/builders/oop/classes_vtables_ops.cpp`، + مسار إصدار المتغيّرات العامّة (`hasLinkSym`). +- التصميم مجموعتان: + - `kReservedRt` (محجوزة **دائمًا** — حرّ ومستضاف): بقيّة القائمة + + `__stack_chk_fail` (اسم دالّة؛ ربط بيانة به غير مشروع). + - `kHostedOnlyReserved` (محجوزة في **المستضاف فقط**): `__stack_chk_guard`. + - الشرط: `kReservedRt.count(sym) || (!cg_.freestanding_ && kHostedOnlyReserved.count(sym))`. +- حراسة التكرار الفعليّ (`getNamedValue`) والرابط تبقى قائمةً فوق ذلك. +- ملاحظة حجم ABI (**قيد صريح**، تخصّ المستهلك لا اللغة): متغيّر ص = `i64` دومًا، + فتُصدَر `@__stack_chk_guard = global i64` (8 بايت)، بينما SSP يقرأ الحارس + `uintptr_t` (4 بايت على i686). البايتات الدنيا هي الحارس على **little-endian**، + والقيمة تُهيَّأ وقت التحميل في `.data` ⇒ لا نافذة حارس صفريّ. **الحاجز:** هذا + السبيل صالح لهدف **i686 صغير-البايتيّة (LE) في الوضع الحرّ حصرًا**؛ أهداف + big-endian أو فحوص تعتمد `sizeof(__stack_chk_guard)==4` تنكسر. اللغة لا تملك + حاليًّا بدائيّة لإصدار عالميّ 32-بت — عند الحاجة لهدف آخر تُضاف كـRFC منفصل. + +# البدائل (Alternatives) + +1. **إبقاء الحجب وترك `__stack_chk_guard` بيانةَ C:** يُبقي ذيلَ C لآليّة هاجر + معالجُها (`__stack_chk_fail`) إلى ص، بلا مبرّر تقنيّ. +2. **إزالة `__stack_chk_guard` من القائمة مطلقًا (بلا شرط الوضع الحرّ):** يفتح في + المستضاف تعريفًا قويًّا يقنّع كوكي SSP لـlibc صامتًا (ثغرة أمنيّة). **مرفوض**؛ + شرط `cg_.freestanding_` هو ما يغلق هذا. +3. **السماح بكلّ الأسماء المحجوزة عند `@رمز` الصريح:** يفقد شبكة الأمان ضدّ + مصادفة `memset`/`malloc`. مرفوض. + +# المخاطر والتخفيف + +- **تقنيع libc-SSP في المستضاف (صامت):** لو أُرخي بلا شرط، يُصدر تعريفًا قويًّا + يستبدل الكوكي العشوائيّ بقيمة ثابتة بلا خطأ ربط (ربط ديناميكيّ) ⇒ تعطيل الحماية. + التخفيف: الإرخاء مشروط بـ`cg_.freestanding_` (مُختبَر: يُرفض في المستضاف). +- **تعريف مزدوج قويّ** (ص + C في الوضع الحرّ): يرصده الرابط بخطأ «duplicate + symbol» ظاهر. أخ.4 تحذف تعريف C وتُبقي `extern` ⇒ تعريف قويّ وحيد. +- **قيد قائم مكتشَف (خارج نطاق هذا المقترح):** خروج المترجم يبقى `0` في + **المستضاف** لأخطاء هذه المرحلة (حجب `memset`/`__stack_chk_guard`): الرمز لا + يُصدَر (‏`continue`) لكنّ رمز الخروج لا يُرفع. سلوك يخصّ كلّ الأسماء المحجوزة لا + هذا التغيير؛ يُقترح إصلاحه لاحقًا (رفع رمز خروج لأخطاء codegen في المستضاف). + +# الاختبارات + +- `test_symbol_stack_guard_allowed_freestanding`: يترجم بنجاح ويُصدر + `@__stack_chk_guard = global i64`. +- `test_symbol_stack_guard_rejected_hosted`: في المستضاف تظهر رسالة الحجب و**لا** + يُصدَر الرمز (حارس الثغرة الأمنيّة). +- `test_symbol_stack_chk_fail_var_rejected`: ربط بيانة باسم الدالّة محجوب في + الوضعين. +- `test_symbol_memset_rejected_hosted` + `test_symbol_reserved_runtime_name_errors` + (قائم): `memset` يبقى محجوبًا في الوضعين — إثبات حصر الإرخاء. +- `test_symbol_stack_guard_duplicate_freestanding_errors`: تكرار الرمز المُرخَّى + في الوضع الحرّ يبقى خطأً (حارس `getNamedValue` فعّال). + +# الأثر على مصدر الحقيقة + +لا تغيير في نحو اللغة ولا في كتالوج المفردات؛ التغيير سلوكُ حارسٍ دلاليّ في +الخلفيّة (codegen) فقط. لا أثر على LSP/التوثيق المولَّد.