اشاره‌گرهای معلق

اشاره‌گرهای معلق در زبانهای برنامه‌نویسی به یک شیء نامشخص اشاره می‌کند که سلامت کد را به خطر می‌اندازد.

Dangling pointer

اشاره‌گر معلق زمانی به وجود می‌آید که یک شی حذف یا آزاد شود، بدون اینکه مقدار اشاره‌گر تغییر شده باشد. در نتیجه آن اشاره‌گر به مکانی اشاره می‌کند که دیگر وجود ندارد. حتی ممکن است سیستم‌عامل مکان قبلی حافظه که آزاد شده بود را به فرایند دیگری داده باشد، در این صورت آن اشاره‌گر به دادهای متفاوت اشاره می‌کند.[۱]

دلایل بروز اشاره‌گرهای معلق ویرایش

در بسیاری از زبان‌های برنامه‌نویسی، مانند سی، وقتی یک شیء از محدوده خارج می‌شود یا صریحاً دستور آزاد شدن دریافت می‌کند، اشاره‌گرهایی که به آن شیء اشاره می‌کنند تغییر نمی‌کنند.

به عنوان مثال به کد زیر توجه کنید:

 char *dp = NULL;
 /* … */
 {
 char c;
 dp = &c;
 } /* c falls out of scope */
 /* dp is now a dangling pointer */

در صورتی که سیستم عامل بتواند اشاره‌گرهایی که به پیوندهای پوچ می‌رسند را شناسایی کند، می‌تواند آن‌ها را برابر ۰ یا NULL قرار دهد. یک راه دیگر این است که به‌طور مثال اجازه داده نشود dp دوباره استفاده شود.

یک راه دیگر به وجود آمدن اشاره‌گر معلق، استفادهٔ همزمان از تابع malloc و در ادامه free است. در واقع بعد از اینکه یک اشاره‌گر با malloc مقداردهی می‌شود و سپس توسط free خالی می‌شود، و اشاره گر استفاده شده معلق می‌شود. به کد زیر توجه کنید:

 char *dp = malloc(A_CONST);
 /* … */
 free(dp); /* dp now becomes a dangling pointer */
 dp = NULL; /* dp is no longer dangling */
 /* … */

یک اشتباه رایج استفاده از آدرس متغیرهای محلی در یک تابع است که با برگرداندن یک اشاره‌گر به آن قسمت، آن اشاره‌گر را معلق می‌کند؛ چرا که اشاره‌گر استفاده شده به جایی از حافظه اشاره می‌کند که پس از اتمام اجرای تابع، آزاد شده و دیگر تضمینی بر حفظ مقادیر وجود ندارد.

 int num = ۱۲۳۴;
 /* … */
 return #

تلاش برای خواندن مقدار برگشت داده شده از تابع، پس از اندکی بعد از فراخوانی ان تابع ممکن است نتیجه ۱۲۳۴ را برگرداند اما در ادامه برنامه احتمالاً آن حافظه برای استفاده دیگری اختصاص خواهد یافت و مقدار مورد نظر ما، پاک خواهد شد.

دلایل به وجود آمدن اشاره‌گرهای وحشی ویرایش

اشاره‌گر وحشی زمانی به وجود می‌آید که اشاره‌گر قبل از اولین استفاده، مقداردهی نشده باشد. در واقع زمانی که یک اشاره‌گر مقداردهی اولیه نشود، آن اشاره‌گر حتماً وحشی خواهد بود. به مثال زیر توجه کنید:

 char *dp; /* dp is a wild pointer */
 static char *scp; /* scp is not a wild pointer:
 * static variables are initialized to 0
 * at start and retain their values from
 * the last call afterwards.
 * Using this feature may be considered bad
 * style if not commented */

سوراخ‌های امنیتی مرتبط ویرایش

مانند سرریز بافر، اشاره‌گرهای معلق به راحتی به سوراخ‌های امنیتی تبدیل می‌شوند. برای مثال اگر یک اشاره‌گر بخواهد یک تابع virtual function را فراخوانی کند، در صورتی که اشاره‌گر دستکاری شده باشد، یک آدرس دیگر می‌تواند فراخوانی شود که ممکن است دستور اجرای یک فرایند خرابکارانه باشد.

روشهای جلوگیری ویرایش

در زبان برنامه‌نویسی C/C++ راحت‌ترین روش قابل استفاده، استفاده از یک تابع جایگزین برای free یا استفاده از تابع delete در زمان مناسب است. به هر حال با این روش، تنها برای اشاره‌گرهایی اثربخش است که مستقیماً توسط توابع تعریف شده خالی شوند، اما سایر اشاره‌گرها را از حال معلق خارج نمی‌سازد.

<syntaxhighlight lang="C">

  1. include <assert.h>
  2. include <stdlib.h>

/* Alternative version for 'free()' */ void safefree(void **pp) {

 /* in debug mode, abort if pp is NULL */
 assert(pp);
 if (pp != NULL) { /* safety check */
 free(*pp); /* deallocate chunk, note that free(NULL) is valid */
 *pp = NULL; /* reset original pointer */
 }
 char *p = NULL, *p2;
 p = (char *)malloc(1000); /* get a chunk */
 p2 = p; /* copy the pointer */
 /* use the chunk here */
 safefree((void **)&p); /* safety freeing; does not affect p2 variable */
 safefree((void **)&p); /* this second call won't fail */
 char c = *p2; /* p2 is still a dangling pointer, so this is undefined behavior. */
 return i + c;

راه حل دیگر این است که قبل از استفاده از malloc() اشاره‌گر بررسی شود و در صورت معلق بودن ۰ مقداردهی شود.

 safefree(&p); /* i'm not sure if chunk has been released */
 p = malloc(1000); /* allocate now */

این راهکارها می‌تواند توسط یک دستور #define با ایجاد چند ماکرو مفید، خلاصه شود. به هر شکل، این برنامه‌نویس است که باید تصمیم بگیرد از توابع امن استفاده کند. در صورت عدم استفاده از این توابع مشکلات اشاره‌گرهای معلق باز هم برمیگردد. یک روش دیگر که قابل استفاده در راهکارهایی که بشتر ساختاریافته هستند استفاده از اشاره‌گر هوشمند است. اشاره گر هوشمند، به این شکل عمل می‌کند که تعداد اشاره‌گرها به یک شی را می‌شمارد و زمانی که نیاز به آزاد سازی حافظه شد، با آزاد سازی ان شی، تمام اشاره‌گرهای ان را هم به NULL تغییر می‌دهد. روش دیگر استفاده از تکنیک Garbage Collection استفاده در این حالت این سیستم عالم است که حساب اشاره‌گرهای برنامه اجرا شده را در اختیار می‌گیرد و در صورتی که شی پیدا شود که اشاره‌گری به ان اشاره نمی‌کند، ان شی را از حافظه پاک می‌کند. این روش به کلی احتمال بروز اشاره‌گرهای معلق را از بین می‌برد.

در زبانهای برنامه‌نویسی سطح بالا، مانند JAVA، امکان بروز اشاره‌گر معلق وجود ندارد، چرا که هیچ روش مستقیمی برای خالی کردن حافظه در این زبان تعبیه نشده و تمام آزاد سازی حافظه توسط Garbage Collector انجام می‌شود.

روش شناسایی ویرایش

یک روش این است که تمام اشاره‌گرها را تا قبل از مقدار دهی برابر ۰ یا null pointer قرار دهیم، همین روش باید برای اشاره‌گرهایی که مقادیر مرتبط با ان آزاد سازی شده‌اند استفاده شود. اشاره گهر null خطری برای استفاده ندارد و در هیچ سیستم کامپیوتری استفاده از ان منجر به از دستکاری داده‌ها و خرابکاری نمی‌شود. در بیشتر کامپایلرها با فراخوانی یا خواندن مقدار null pointer برنامه متوقف می‌شود و ادامه نمی‌یابد.

منابع ویرایش

  1. "Warning Options - Using the GNU Compiler Collection (GCC)".