سؤال

أقوم بإنشاء برنامج يستخدم mprotect () لتقييد كتلة الذاكرة من الوصول. عند طلب الذاكرة ، يتم إلقاء sigsegv والتي أستمع إليها باستخدام مكالمة Signal ().

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

void fifoSigHandler(){

    // Needs to only remove protection from requested block of virtual memory
    mprotect(fifoVm,(size_t)fifoVm_size,PROT_WRITE);
    printf("Caught Seg Fault");
}

void fifo_init(void* vm, int vm_size, int n_frames, int page_size)
{
    fifoVm = vm;
    fifoVm_size = vm_size;
    fifoFrames = n_frames;
    fifoPageSize = page_size;

    mprotect(fifoVm,(size_t)fifoVm_size,PROT_NONE);

    signal(SIGSEGV, fifoSigHandler);
}

بالإضافة إلى ذلك ، هل هناك طريقة لتحديد مستوى mProtect () تم تعيين كتلة من الذاكرة حاليًا (prot_none ، prot_read ، إلخ.)؟

هل كانت مفيدة؟

المحلول

عليك أن تستخدم sigaction مع SA_SIGINFO بدلاً من signal لتأسيس معالجك ، وبعد ذلك سوف يتم الاتصال بك بمعلومات مفيدة في أ siginfo_t, ، بما فيها si_addr.

si_addr, ، كما هو موضح في sigaction(2) ، سوف تحتوي على العنوان. بالنسبة للطول ، حسنًا ، أنت محظوظ إلا إذا كنت على استعداد لتحليل التعليمات. أفضل ما يمكنك القيام به هو اتخاذ إجراء للصفحة المبلغ عنها في si_addr, ، ثم إذا لم يكن ذلك كافيًا ، فستحصل على إشارة أخرى قريبًا بما فيه الكفاية. على الأقل ، هكذا فعلنا الأشياء في ObjectStore.

نصائح أخرى

أنت تبحث عن libsigsegv http://libsigsegv.sourceforge.net/

لكن احذر من ذلك الاتصال mprotect هو فقط آمن للإشارة في Linux ، قد لا تدعم أنظمة POSIX الأخرى هذا.

أخشى أنه في Linux ، فإن الطريقة الوحيدة للحصول على بتات حماية الذاكرة هي القراءة في /proc/$pid/meminfo

على ملاحظة جانبية (Linux فقط): إذا كنت قلقًا بشأن استهلاك الذاكرة وتعتزم تمكين صفحات رسم خرائط أكبر واحدًا تلو الآخر ، فأنا أنصح بإنشاء رسم الخرائط الخاص بك باستخدام mmap مع MAP_NORESERVE في هذه الحالة ، ستحصل على رسم خرائط لصفحات النسخ المملوءة بالصفر والتي ستخصص ذاكرة الوصول العشوائي المادية في الكتابة الأولى. MAP_NORESERVE يرشد kernel بعدم دعم ذاكرتك بمساحة المبادلة مما يتيح لك تخصيص ما يصل إلى 64 تيرابايت من مساحة العنوان الظاهري. الجانب السلبي هو أنه إذا نفدت من الذاكرة ، فيمكن أن تحدث أشياء فظيعة (قاتل OOM).

الخطوة 1: init sigaction:

struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
sigemptyset(&act.sa_mask);
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;

الخطوة 2: اصنع هذا sigaction يتعامل SIGSEGV:

sigaction(SIGSEGV, &act, NULL);

(اختياري) الخطوه 3: اجعلها تتعامل مع إشارات الذاكرة الأخرى أيضًا:

sigaction(SIGBUS, &act, NULL);
sigaction(SIGTRAP, &act, NULL);

أضف معالجة الخطأ حسب الضرورة

الخطوة 4: تحديد وظيفة المعالج:

void handler(int signal, siginfo_t* siginfo, void* uap) {
    printf("Attempt to access memory at address %p\n", 
           siginfo->si_addr);
    #ifdef LINUX_64BIT
    printf("Instruction pointer: %p\n",
           (((ucontext_t*)uap)->uc_mcontext.gregs[16]));
    #elif LINUX_32BIT
    printf("Instruction pointer: %p\n",
           (((ucontext_t*)uap)->uc_mcontext.gregs[14]));
    #endif
}

يمكنك الرجوع إلى صفحات الرجل ucontext_t و siginfo_t للحصول على بيانات أكثر إثارة للاهتمام يمكن أن يستخرج معالجك.

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