مؤشر متدلٍ

المؤشرات المعلقة والمؤشرات الجامحة في برمجة الحاسوب هي مؤشرات لا تشير إلى كائن صالح من النوع المناسب. وهي حالات خاصة من انتهاكات أمان الذاكرة . وبشكل أعم، المراجع المعلقة والمراجع الجامحة هي مراجع لا تُحل إلى وجهة صالحة.
تنشأ المؤشرات المعلقة أثناء عملية حذف الكائنات ، عندما يُحذف كائنٌ يُشير إليه مؤشرٌ مُعين أو يُلغى تخصيصه دون تعديل قيمة ذلك المؤشر، بحيث يبقى المؤشر مُشيرًا إلى موقع الذاكرة التي أُلغي تخصيصها. قد يُعيد النظام تخصيص الذاكرة التي أُحررت سابقًا، وإذا حاول البرنامج بعد ذلك الوصول إلى المؤشر المعلق، فقد ينتج عن ذلك سلوكٌ غير متوقع ، إذ قد تحتوي الذاكرة الآن على بياناتٍ مختلفة تمامًا. إذا كتب البرنامج إلى ذاكرةٍ يُشير إليها مؤشرٌ معلق، فقد ينتج عن ذلك تلفٌ صامتٌ لبياناتٍ غير ذات صلة، مما يؤدي إلى أخطاءٍ دقيقة يصعب اكتشافها للغاية. إذا أُعيد تخصيص الذاكرة لعمليةٍ أخرى، فإن محاولة الوصول إلى المؤشر المعلق قد تُسبب أخطاء تجزئة (في أنظمة UNIX وLinux) أو أخطاء حمايةٍ عامة (في نظام Windows). إذا كان لدى البرنامج صلاحياتٌ كافيةٌ تسمح له بالكتابة فوق بيانات إدارة الذاكرة التي يستخدمها مُخصص الذاكرة في النواة، فقد يُسبب هذا التلف عدم استقرار النظام. في لغات البرمجة كائنية التوجه التي تعتمد على جمع البيانات المهملة ، تُمنع المراجع المعلقة عن طريق تدمير الكائنات التي لا يمكن الوصول إليها فقط، أي التي لا تحتوي على أي مؤشرات واردة؛ ويتم ضمان ذلك إما عن طريق التتبع أو عدّ المراجع . مع ذلك، قد يُنشئ المُنهي مراجع جديدة لكائن ما، مما يستلزم إعادة إحياء الكائن لمنع حدوث مرجع معلق.
تظهر المؤشرات الجامحة، والتي تُسمى أيضًا المؤشرات غير المُهيأة، عندما يُستخدم مؤشر قبل تهيئته إلى حالة معروفة، وهو أمر ممكن في بعض لغات البرمجة. وتُظهر هذه المؤشرات نفس السلوك غير المنتظم للمؤشرات المعلقة، على الرغم من أنها أقل عرضة للاختفاء عن الأنظار لأن العديد من المترجمات تُصدر تحذيرًا أثناء الترجمة إذا تم الوصول إلى المتغيرات المُعلنة قبل تهيئتها. [ 1 ]
سبب تذبذب المؤشرات
في العديد من لغات البرمجة (مثل لغة C )، لا يؤدي حذف كائن من الذاكرة صراحةً أو بتدمير إطار المكدس عند العودة إلى تغيير المؤشرات المرتبطة به. يبقى المؤشر يشير إلى نفس الموقع في الذاكرة حتى وإن كان هذا الموقع يُستخدم الآن لأغراض أخرى.
يُعرض أدناه مثال بسيط:
{ char * dp = NULL ; // ... { char c ; dp = & c ; } // c خارج نطاق التعريف // dp الآن مؤشر معلق }إذا كان نظام التشغيل قادرًا على اكتشاف الإشارات إلى المؤشرات الفارغة أثناء التشغيل ، فإن أحد الحلول المذكورة أعلاه هو إسناد القيمة 0 (فارغة) إلى المؤشر dp مباشرةً قبل الخروج من الكتلة الداخلية. حل آخر هو ضمان عدم استخدام المؤشر dp مرة أخرى دون تهيئة إضافية.
من الأسباب الشائعة الأخرى لظهور المؤشرات المعلقة هو التداخل بين malloc()استدعاءات free()المكتبات: يصبح المؤشر معلقًا عند تحرير كتلة الذاكرة التي يشير إليها. وكما في المثال السابق، تتمثل إحدى طرق تجنب ذلك في التأكد من إعادة تعيين المؤشر إلى قيمة فارغة (null) بعد تحرير مرجعه، كما هو موضح أدناه.
#include <stdlib.h>void func () { char * dp = ( char * ) malloc ( sizeof ( char ) * 10 ); // ... free ( dp ); // dp الآن مؤشر معلق dp = NULL ; // dp لم يعد معلقًا // ... }من الأخطاء الشائعة جدًا إرجاع عناوين متغير محلي مخصص في المكدس: بمجرد أن تعود الدالة المستدعاة، يتم إلغاء تخصيص المساحة لهذه المتغيرات، ومن الناحية الفنية، يكون لها "قيم غير صالحة".
int * func ( void ) { int num = 1234 ; // ... return & num ; }قد تُعيد محاولات القراءة من المؤشر القيمة الصحيحة (1234) لفترة وجيزة بعد استدعاء الدالة func، ولكن أي دوال تُستدعى لاحقًا قد تستبدل مساحة التخزين المخصصة للمؤشر numبقيم أخرى، وبالتالي لن يعمل المؤشر بشكل صحيح. إذا كان numلا بد من إعادة مؤشر إلى الدالة، numفيجب أن يكون نطاقه أوسع من نطاق الدالة - ويمكن تعريفه على أنه مؤشر static.
إلغاء التخصيص اليدوي بدون مرجع معلق
ابتكر أنطوني كريتشمار (1945-1996) نظامًا متكاملًا لإدارة الكائنات خالٍ من ظاهرة المراجع المعلقة. [ 2 ] وقد اقترح فيشر وليبلانك نهجًا مشابهًا [ 3 ] تحت اسم "الأقفال والمفاتيح" .
سبب المؤشرات الجامحة
تُنشأ المؤشرات الجامحة بحذف عمليات التهيئة اللازمة قبل الاستخدام الأول. وبالتالي، من الناحية الدقيقة، يبدأ كل مؤشر في لغات البرمجة التي لا تفرض التهيئة كمؤشر جامح.
يحدث هذا غالبًا بسبب تجاوز عملية التهيئة، وليس بسبب حذفها. تستطيع معظم المترجمات التنبيه إلى ذلك.
int f ( int i ) { char * dp ; // dp مؤشر عام static char * scp ; /* scp ليس مؤشرًا عامًا: * يتم تهيئة المتغيرات الثابتة إلى 0 * عند بدء التشغيل وتحتفظ بقيمها من * آخر استدعاء بعد ذلك. * قد يُعتبر استخدام هذه الميزة أسلوبًا سيئًا * إذا لم يتم التعليق عليه */ }ثغرات أمنية تتعلق بمؤشرات متدلية
على غرار ثغرات تجاوز سعة المخزن المؤقت ، تُصبح ثغرات المؤشرات المعلقة/غير المُستخدمة بكثرة ثغرات أمنية. فعلى سبيل المثال، إذا استُخدم المؤشر لاستدعاء دالة افتراضية ، فقد يتم استدعاء عنوان مختلف (ربما يُشير إلى شيفرة استغلال) نتيجةً للكتابة فوق مؤشر جدول الدوال الافتراضية . أو إذا استُخدم المؤشر للكتابة إلى الذاكرة، فقد تتلف بنية بيانات أخرى. حتى لو لم تُقرأ الذاكرة إلا بعد أن يُصبح المؤشر معلقًا، فقد يؤدي ذلك إلى تسريب معلومات (إذا وُضعت بيانات مهمة في البنية التالية المُخصصة هناك) أو إلى رفع مستوى الصلاحيات (إذا استُخدمت الذاكرة غير الصالحة في عمليات التحقق الأمني). عندما يُستخدم مؤشر معلق بعد تحريره دون تخصيص جزء جديد من الذاكرة له، يُعرف هذا باسم ثغرة "الاستخدام بعد التحرير". [ 4 ] على سبيل المثال، CVE - 2014-1776 هي ثغرة أمنية من نوع "استخدام بعد التحرير" في متصفح مايكروسوفت إنترنت إكسبلورر من الإصدار 6 إلى 11 [ 5 ] والتي استُخدمت في هجمات اليوم الصفر بواسطة تهديد مستمر متقدم . [ 6 ]
تجنب أخطاء المؤشر المعلق
في لغة C، أبسط طريقة هي استخدام نسخة بديلة من free()الدالة (أو ما شابهها) تضمن إعادة ضبط المؤشر. مع ذلك، لن تُفرغ هذه الطريقة متغيرات المؤشر الأخرى التي قد تحتوي على نسخة من المؤشر.
#include <assert.h> #include <stdlib.h>// نسخة آمنة من الدالة free() static void safeFree ( void ** pp ) { // في وضع التصحيح، يتم الإجهاض إذا كانت قيمة pp تساوي NULL assert ( pp ); // تعمل الدالة free(NULL) بشكل صحيح، لذا لا يلزم إجراء أي فحص إضافي في وضع التصحيح free ( * pp ); // تحرير جزء من الذاكرة، لاحظ أن free(NULL) صالحة * pp = NULL ; // إعادة تعيين المؤشر الأصلي }int f ( int i ) { char * p = NULL ; char * p2 ; p = ( char * ) malloc ( 1000 ); // الحصول على جزء من الذاكرة p2 = p ; // نسخ المؤشر // استخدام الجزء هنا safeFree (( void ** ) & p ); // تحرير آمن؛ لا يؤثر على المتغير p2 safeFree (( void ** ) & p ); // لن تفشل هذه الاستدعاء الثاني لأن p يُعاد تعيينه إلى NULL char c = * p2 ; // لا يزال p2 مؤشرًا معلقًا، لذا فهذا سلوك غير مُعرَّف. return i + c ; }يمكن استخدام النسخة البديلة حتى لضمان صحة المؤشر الفارغ قبل الاستدعاء malloc():
safeFree ( & p ); // لست متأكدًا مما إذا تم تحرير الجزء */ p = ( char * ) malloc ( 1000 ); // تخصيص الذاكرة الآنيمكن إخفاء هذه الاستخدامات من خلال #defineتوجيهات لإنشاء وحدات ماكرو مفيدة (منها على سبيل المثال #define XFREE(ptr) safeFree((void**)&(ptr)))، مما يُنشئ ما يشبه لغة وصفية، أو يمكن تضمينها في مكتبة أدوات منفصلة. في جميع الأحوال، يجب على المبرمجين الذين يستخدمون هذه التقنية استخدام الإصدارات الآمنة في كل حالة free()يتم فيها استخدام هذه الوحدة؛ وإلا سيعود الأمر إلى المشكلة. كما أن هذا الحل يقتصر على نطاق برنامج أو مشروع واحد، ويجب توثيقه بشكل صحيح.
من بين الحلول الأكثر تنظيمًا، تُعدّ المؤشرات الذكية تقنية شائعة لتجنب مشكلة المؤشرات المعلقة في لغة C++ . تستخدم المؤشرات الذكية عادةً عدّ المراجع لاستعادة الكائنات. وتشمل بعض التقنيات الأخرى طريقة علامات الحذف وطريقة الأقفال والمفاتيح . [ 3 ]
ثمة نهج آخر يتمثل في استخدام جامع البيانات المهملة Boehm ، وهو جامع بيانات مهملة محافظ يستبدل دوال تخصيص الذاكرة القياسية في لغتي C و C++ بجامع بيانات مهملة. يقضي هذا النهج تمامًا على أخطاء المؤشرات المعلقة عن طريق تعطيل عمليات التحرير، واستعادة الكائنات من خلال جمع البيانات المهملة.
ثمة نهج آخر يتمثل في استخدام نظام مثل CHERI ، الذي يخزن المؤشرات مع بيانات وصفية إضافية قد تمنع الوصول غير الصحيح من خلال تضمين معلومات دورة حياة المؤشر. يتطلب CHERI عادةً دعمًا في وحدة المعالجة المركزية لإجراء هذه الفحوصات الإضافية.
في لغات مثل جافا، لا يمكن أن تحدث مشكلة المؤشرات المعلقة لعدم وجود آلية لتحرير الذاكرة بشكل صريح. بدلاً من ذلك، قد يقوم جامع البيانات المهملة بتحرير الذاكرة، ولكن فقط عندما يصبح الكائن غير قابل للوصول إليه من أي مرجع.
في لغة Rust ، تم توسيع نظام الأنواع ليشمل أيضًا دورات حياة المتغيرات، كما يتم تهيئة الموارد . ما لم يتم تعطيل هذه الميزات في اللغة، سيتم اكتشاف المؤشرات المعلقة أثناء الترجمة والإبلاغ عنها كأخطاء برمجية.
اكتشاف المؤشر المعلق
لكشف أخطاء المؤشرات المعلقة، تتمثل إحدى تقنيات البرمجة الشائعة في تعيين مؤشرات إلى مؤشر فارغ أو إلى عنوان غير صالح بمجرد تحرير المساحة التي تشير إليها. عند إلغاء مرجعية المؤشر الفارغ (في معظم لغات البرمجة)، سيتوقف البرنامج فورًا، فلا مجال لتلف البيانات أو حدوث سلوك غير متوقع. هذا يُسهّل العثور على الخطأ البرمجي الأساسي وحلّه. لكن هذه التقنية لا تُجدي نفعًا عند وجود نسخ متعددة من المؤشر.
تقوم بعض أدوات تصحيح الأخطاء تلقائيًا بالكتابة فوق البيانات التي تم تحريرها وتدميرها، عادةً بنمط محدد، مثل (على سبيل المثال ، 0xDEADBEEFيستخدم مصحح أخطاء Visual C/C++ من مايكروسوفت النمط أو حسب البيانات التي تم تحريرها [ 7 ] ). يمنع هذا عادةً إعادة استخدام البيانات بجعلها غير ضرورية وبارزة للغاية (يُظهر النمط للمبرمج أن الذاكرة قد تم تحريرها بالفعل).0xCC0xCD0xDD
يمكن أيضًا استخدام أدوات مثل Polyspace و TotalView و Valgrind و Mudflap و AddressSanitizer [ 8 ] أو الأدوات القائمة على LLVM [ 9 ] للكشف عن استخدامات المؤشرات المعلقة.
تقوم أدوات أخرى ( SoftBound و Insure++ و CheckPointer ) بتجهيز التعليمات البرمجية المصدرية لجمع وتتبع القيم المشروعة للمؤشرات ("البيانات الوصفية") والتحقق من كل عملية وصول إلى المؤشر مقابل البيانات الوصفية للتأكد من صحتها.
تتمثل إحدى الاستراتيجيات الأخرى، عند الاشتباه في مجموعة صغيرة من الفئات، في جعل جميع وظائفها الأعضاء افتراضية مؤقتًا : بعد تدمير/تحرير مثيل الفئة، يتم تعيين مؤشره إلى جدول الأساليب الافتراضيةNULL ، وأي استدعاء لوظيفة عضو سيؤدي إلى تعطل البرنامج وسيظهر الكود المذنب في مصحح الأخطاء.
يؤدي ملحق وضع علامات الذاكرة ARM64 (MTE) - المعطل افتراضيًا على أنظمة Linux، ولكن يمكن تفعيله على Android 16 - إلى حدوث خطأ في تجزئة الذاكرة عند اكتشافه استخدام الذاكرة بعد تحريرها وتجاوز سعة المخزن المؤقت . [ 10 ] [ 11 ]
انظر أيضاً
مراجع
- ↑ "خيارات التحذير - استخدام مجموعة مترجمات GNU (GCC)" .
- ↑ جيانا سيوني، أنتوني كريتشمار، إلغاء تخصيص مبرمج بدون مرجع معلق ، رسائل معالجة المعلومات ، المجلد 18، 1984 ، الصفحات 179-185
- 1 2 C. N. Fisher, RJ Leblanc, The implementation of run-time diagnostics in Pascal , IEEE Transactions on Software Engineering , 6(4):313–319, 1980.
- ↑ دالسي، إريك؛ مؤلف مجهول؛ فريق محتوى CWE (11 مايو 2012). "CWE-416: استخدام بعد التحرير" . تعداد نقاط الضعف الشائعة . مؤسسة ميتري . تم الاسترجاع في 28 أبريل 2014 .
{{cite web}}له|author2=اسم عام ( مساعدة ) - ↑ "CVE-2014-1776" . الثغرات الأمنية الشائعة (CVE) . 29 يناير 2014. مؤرشف من الأصل في 30 أبريل 2017. تم الاطلاع عليه في 16 مايو 2017 .
- ↑ تشين، شياوبو؛ كاسيلدين، دان؛ سكوت، مايك (26 أبريل 2014). "اكتشاف ثغرة أمنية جديدة من نوع Zero-Day تستهدف متصفح إنترنت إكسبلورر من الإصدار 9 إلى 11 في هجمات مُستهدفة" . مدونة فاير آي . فاير آي . تاريخ الاطلاع: 28 أبريل 2014 .
- ↑ أنماط ملء الذاكرة في Visual C++ 6.0
- ↑ تصحيح أخطاء مؤشر رفرف الطين
- ↑ دهورجاتي، د. وأدفي، ف. الكشف الفعال عن جميع استخدامات المؤشرات المعلقة في خوادم الإنتاج
- ↑ "امتداد وضع علامات الذاكرة لـ ARM" . مشروع Android مفتوح المصدر . تم الاسترجاع في 11 يونيو 2025 .
- ↑ غودين، دان (13 مايو 2025). "جوجل تُطلق وضع الحماية المتقدمة لمستخدمي أندرويد الأكثر عرضة للخطر" . آرس تكنيكا . تاريخ الاسترجاع: 11 يونيو 2025 .
- أخطاء برمجية
- ثغرات أمنية في الحاسوب
- المؤشرات (برمجة الحاسوب)
