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

اشاره‌گر‌های معلق در زبانهای برنامه نویسسی اشاره گرهایی که به یک شی مشخص اشاره نمی‌کنند. شرایط مختلفی به وجود میاید که سلامت کد را به خطر میندازد.
اشاره‌گر معلق زمانی به وجود می‌آید که یک شی حذف یا آزادسازی می‌شود، بدون اینکه مقدار یک اشاره‌گر تغییر داده شده باشد. در نتیجه آن اشاره‌گر به مکانی اشاره می‌کند که دیگر وجود ندارد. حتی ممکن است سیستم مکان قبلی حافظه که ازاد شده بود را به فرایند دیگری داده باشد، در این صورت ان اشارهگر به دادهای متفاوت اشاره میکند. بیشتر زمانی این اتفاق میفتدکه برنامه سعی می‌کند داده‌هایی روی حافظه بنویسد. در این صورت ممکن است برنامه اطلاعاتی را تغییر میدهد و باعث بروز باگهایی در خود ان برنامه یا برنامه‌های دیگر شود که تشخیص ان کار ساده‌ای نیست.
اشارهگر‌های معلق زمانی به وجود میایند که یک اشارهگر قبل از مقداردهی اولیه یک متغیر به وجود میاید. ممکن است در یک زبان برنامه نویسی این اتفاق میفتد در این صورت احتمال خطایابی و رفع ان بیشتر میشود.
دلایل بروز اشارهگرهای معلق
در بسیاری از زبان‌های برنامه نویسی، مانند C، وقتی یک شی از محدوده خارج میشود یا صریحا دستور خالی شدن دریافت میکند، اشارهگرهایی که به ان شی اشاره میکنند تغییر نمی‌کنند.
به عنوان مثال به کد زیر توجه کنید:
char *dp = NULL;
char c;
dp =
/* c falls out of scope */
/* dp is now a dangling pointer */
در صورتی که سیستم عامل بتواند اشاره‌گرهایی که به پیوندهای پوچ میرسند، شناسایی کند، میتواند انها را برابر 0 یا NULL قرار دهد. یک راه دیگر این است که به طور مثال اجازه داده نشود dp دوباره استفاده شود.
یک راه دیگر بوجود امدن اشاره‌گر معلق، استفاده همزمان تابع malloc و در ادامه free است. در واقع بعد از اینکه یک اشارهگر با malloc مقداردهی میشود توسط free خالی میشود و اشارهگر استفاده شده معلق میشود. به کد زیر توجه کنید:
include
void func()
char *dp = malloc(A_CONST);
free(dp); /* dp now becomes a dangling pointer */
dp = NULL; /* dp is no longer dangling */
یک اشتباه رایج استفاده از ادرس متغیرهای محلی در یک تابع است که با برگرداندن یک اشارهگر به ان قسمت، ان اشارهگر را معلق میکند. چرا که اشارهگر استفاده شده به جایی از حافظه اشاره میکند که پس از اتمام اجرای تابع خالی شده و دیگر تضمینی بر حفظ مقادیر وجود ندارد.
int *func(void)
int num = 1234;
return
تلاش برای خواندن مقدار برگشت داده شده از تابع، پس از اندکی بعد از فراخوانی ان تابع ممکن است نتیجه 1234 را برگرداند اما در ادامه برنامه احتمالا ان حافظه برای استفاده دیگری اختصاص خواهد یافت و مقدار مورد نظر ما، پاک خواهد شد.
دلایل به وجود امدن اشارهگرهای وحشی
اشارهگرهای وحشی زمانی به وجود میایند که اشارهگرها قبل از اولین استفاده، مقداردهی نشده باشند. در واقع زمانی که یک اشارهگر مقداردهی اولیه نمی‌شود، ان اشارهگر حتما وحشی است. به مثال زیر توجه کنید:
int f(int i)
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 در زمان مناسب است. به هر حال با این روش، تنها برای یک اشارهگرهایی اثربخش است که مستقیما توسط توابع تعریف شده خالی شوند، اما سایر اشارهگرها را از حال معلق خارج نمی‌سازد.
include
include
/* 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 */
int f(int i)
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 wont fail */
char c = *p2; /* p2 is still a dangling pointer, so this is undefined behavior. */
return i + c;
راه حل دیگر این است که قبل از استفاده از malloc() اشارهگر بررسی شود و در صورت معلق بودن 0 مقداردهی شود.
safefree(-p); /* im not sure if chunk has been released */
p = malloc(1000); /* allocate now */
این راهکارها میتواند توسط یک دستور #define با ایجاد چند ماکرو مفید، خلاصه شود. به هر شکل، این برنامه نویس است که باید تصمیم بگیرد از توابع امن استفاده کند. در صورت عدم استفاده از این توابع مشکلات اشارهگرهای معلق باز هم برمیگردد.
یک روش دیگر که قابل استفاده در راهکارهایی که بشتر ساختاریافته هستند استفاده از اشارهگر هوشمند است. اشارهگر هوشمند، به این شکل عمل میکند که تعداد اشارهگرها به یک شی را میشمارد و زمانی که نیاز به ازاد سازی حافظه شد، با ازاد سازی ان شی، تمام اشارهگرهای ان را هم به NULL تغییر میدهد.
روش دیگر استفاده از تکنیک Garbage Collection استف. در این حالت این سیستم عالم است که حساب اشارهگرهای برنامه اجرا شده را در اختیار میگیرد و در صورتی که شی پیدا شود که اشارهگری به ان اشاره نمی‌کند، ان شی را از حافظه پاک میکند. این روش به کلی احتمال بروز اشارهگرهای معلق را از بین میبرد.
در زبانهای برنامه نویسی سطح بالا، مانند JAVA، امکان بروز اشارهگر معلق وجود ندارد، چرا که هیچ روش مستقیمی برای خالی کردن حافظه در این زبان تعبیه نشده و تمام ازادسازی حافظه توسط Garbage Collector انجام میشود.
روش شناسایی
یک روش این است که تمام اشارهگرها را تا قبل از مقدار دهی برابر 0 یا null pointer قرار دهیم، همین روش باید برای اشارهگرهایی که مقادیر مرتبط با ان ازادسازی شده‌اند استفاده شود. اشارهگهر null خطری برای استفاده ندارد و در هیچ سیستم کامپیوتری استفاده از ان منجر به از دستگاری داده‌ها و خرابکاری نمی‌شود. در بیشتر کامپایلرها با فراخوانی یا خواندن مقدار null pointer برنامه متوقف میشود و ادامه نمی‌یابد.