باستخدام خطافات glibc malloc بطريقة آمنة الموضوع

StackOverflow https://stackoverflow.com/questions/2020997

  •  19-09-2019
  •  | 
  •  

سؤال

أرغب في مراقبة استخدام Mallocs و Free في تطبيق باستخدام MOROC والسنانير المجانية.

إليك الوثائق http://www.gnu.org/s/libc/manual/html_node/hooks-for-malloc.html.

من صفحة المثال، يمكنك أن ترى ذلك my_malloc_hook يقوم Transientyly بتحويل HOMAC HOCK (أو إلى الخطاف السابق في السلسلة) قبل إعادة استدعاء malloc.

هذه مشكلة عند مراقبة التطبيقات متعددة الخيوط (انظر نهاية السؤال للحصول على شرح).

أمثلة أخرى لاستخدام خطاف Malloc الذي وجدته على الإنترنت له نفس المشكلة.

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

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

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

يقول المواصفات تصميمي لا أستطيع استبدال malloc بتصميم malloc مختلف.

أستطيع أن أفترض أنه لا توجد خطافات أخرى في اللعب.


تحديث

نظرا لأن هوك Malloc قد تمت إزالته مؤقتا أثناء قيامه بخدمة MOLOC، فقد يتصل مؤشر ترابط آخر بالملق وعدم الحصول على الخطاف.

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

caller -> 
  malloc -> 
    malloc-hook (disables hook) -> 
      malloc -> # possible hazard starts here
        malloc_internals
      malloc <-
    malloc-hook (enables hook) <-
  malloc
caller
هل كانت مفيدة؟

المحلول

محدث

أنت حق عدم الثقة __malloc_hooks؛ لقد نظرت في الكود، وهم - بجنون مذهلون - وليس خيط آمن.

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

من عند http://manpages.sgvulcan.com/malloc_hook.3.php.:

متغيرات هوك ليست آمنة للخيط بحيث يتم إهمالها الآن. يجب على المبرمجين بدلا من استباقية المكالمات إلى الوظائف ذات الصلة من خلال تحديد وتصدير الوظائف مثل "malloc" و "مجانا".

الطريقة المناسبة لحقن MOLLOC / REALLOC / المجاني هي توفير مكتبة خاصة بك تقوم بتصدير إصدارات "تصحيح الأخطاء" الخاصة بك من هذه الوظائف، ثم تعرف نفسها على الأنواع الحقيقية. يتم ربط الارتباط C في ترتيب صريح، لذلك إذا قدمت المكتبات نفس الوظيفة، يتم استخدام أول محددة. يمكنك أيضا ضخ MALOC الخاص بك في وقت التحميل على UNIX باستخدام آليات LD_PRELOAD.

http://linux.die.net/man/3/efence. يصف السياج الكهربائي، والتي تفاصيل كل من هذه الأساليب.

يمكنك استخدام قفلك الخاص إذا كان في هذه الوظائف تصحيح هذه إذا كان ذلك ضروريا.

نصائح أخرى

لدي نفس المشكلة. لقد حلها مع هذا المثال. إذا لم نحدد Thread_safe، لدينا المثال الذي قدمه الرجل، ولدينا خطأ تجزئة. إذا حددنا الخيط_safe، فلن يكون لدينا خطأ تجزئة.

#include <malloc.h>
#include <pthread.h>

#define THREAD_SAFE
#undef  THREAD_SAFE

/** rqmalloc_hook_  */

static void* (*malloc_call)(size_t,const void*);

static void* rqmalloc_hook_(size_t taille,const void* appel)
{
void* memoire;

__malloc_hook=malloc_call; 
memoire=malloc(taille);    
#ifndef THREAD_SAFE
malloc_call=__malloc_hook;   
#endif
__malloc_hook=rqmalloc_hook_; 
return memoire;
}

/** rqfree_hook_ */   

static void  (*free_call)(void*,const void*);

static void rqfree_hook_(void* memoire,const void* appel)
{
__free_hook=free_call;   
free(memoire);            
#ifndef THREAD_SAFE
free_call=__free_hook;    
#endif
__free_hook=rqfree_hook_; 
}

/** rqrealloc_hook_ */

static void* (*realloc_call)(void*,size_t,const void*);

static void* rqrealloc_hook_(void* memoire,size_t taille,const void* appel)
{
__realloc_hook=realloc_call;     
memoire=realloc(memoire,taille); 
#ifndef THREAD_SAFE
realloc_call=__realloc_hook;    
#endif
__realloc_hook=rqrealloc_hook_; 
return memoire;
}

/** memory_init */

void memory_init(void)
{
  malloc_call  = __malloc_hook;
  __malloc_hook  = rqmalloc_hook_;

  free_call    = __free_hook;
  __free_hook    = rqfree_hook_;

  realloc_call = __realloc_hook;
  __realloc_hook = rqrealloc_hook_;
 }

 /** f1/f2 */

 void* f1(void* param)
 {
 void* m;
 while (1) {m=malloc(100); free(m);}
 }

 void* f2(void* param)
 {
 void* m;
 while (1) {m=malloc(100); free(m);}
 }

 /** main */
 int main(int argc, char *argv[])
 {
 memory_init();
 pthread_t t1,t2;

 pthread_create(&t1,NULL,f1,NULL);
 pthread_create(&t1,NULL,f2,NULL);
 sleep(60);
 return(0);
 }

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

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

edit2] بعد بعض التفكير، أعتقد أن هذا الجزء من مسار المكالمات يجب أن يكون محمي بواسطة قفل من نوع ما يخلقه Glibc لك. خلاف ذلك، فإن استخدام السنانير في التعليمات البرمجية متعددة الخيوط لن يعمل أبدا بشكل موثوق وأنا متأكد من أن المستندات سيتذكر ذلك. حيث malloc() يجب أن تكون آمنة الموضوع، يجب أن تكون السنانير كذلك.

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

edit3] إذا فشل الاختبار، فبسبب وضعك القانوني، فليس من الممكن تنفيذ الشاشة. أخبر رئيسك ودعه اتخاذ قرار بشأنه.

Edit4] قام Googling بتحويل هذا التعليق من تقرير الأخطاء:

السنانير ليست آمنة الموضوع. فترة. ما الذي تحاول إصلاحه؟

هذا جزء من مناقشة من مارس 2009 عن علة في libc/malloc/malloc.c التي تحتوي على إصلاح. لذلك ربما نسخة من glibc بعد هذا التاريخ يعمل ولكن لا يبدو أن هناك ضمانا. يبدو أيضا أنه يعتمد على نسختك من دول مجلس التعاون الخليجي.

لا توجد وسيلة لاستخدام خطافات malloc بطريقة آمنة للخيط بينما تكرر في malloc. الواجهة مصممة بشكل سيء، وربما وراء الإصلاح.

حتى إذا وضعت mutex في رمز الخطاف الخاص بك، فإن المشكلة هي أن المكالمات malloc لا ترى تلك الأقفال حتى بعد مرورها من خلال آلية الخطاف، والمرور عبر آلية الخطاف، فإنها تنظر إلى المتغيرات العالمية (مؤشرات الخطاف) دون الحصول على Mutex الخاص بك. أثناء حفظ هذه المؤشرات وتغييرها واستعادتها في مؤشرات مؤشر ترابط واحد، تتأثر مكالمات مخصص في مؤشر ترابط آخر.

مشكلة التصميم الرئيسية هي أن السنانير هي مؤشرات فارغة بشكل افتراضي. إذا زودت الواجهة ببساطة السنانير الافتراضية غير الفارغة التي هي مناسبة المكونة (المخصصات ذات المستوى السفلي الذي لا يدعو أي خطاف آخر)، فسيكون ذلك بسيطا وآمنا لإضافة السنانير: يمكنك فقط حفظ السنانير السابقة، وفي السنانير الجديدة، recurse إلى Malloc عن طريق استدعاء السنانير HOLD، دون هراء بأي مؤشرات عالمية (بخلاف وقت تثبيت الخطاف، والتي يمكن القيام بها قبل بدء أي مواضيع).

بدلا من ذلك، يمكن أن توفر Glibc واجهة Malloc الداخلية التي لا تستدعي السنانير.

سيكون التصميم العاقل آخر هو استخدام التخزين المحلي للخيط للسنانير. سيتم تجاوز واستعادة الخطاف في مؤشر ترابط واحد، دون إزعاج السنانير التي يراها مؤشر ترابط آخر.

كما تقف، ما يمكنك القيام به لاستخدام هوك glibc malloc بأمان هو تجنب recursing في malloc. لا تقم بتغيير مؤشرات الخطافة داخل عمليات الاسترجاعات هوك، وتبصل ببساطة على مصلحك الخاص.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top