Пример 11

/* Пакет для ловли наездов областей выделенной памяти
 * друг на друга,
 * а также просто повреждений динамически отведенной памяти.
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>      /* O_RDWR */
#include <sys/types.h>
#include <ctype.h>
#include <locale.h>

#define CHECKALL
/*
        ----------------- <--------- ptr
        | red_zone      | головная "пограничная зона"
        ----------------        | byte[0]       |
        |     ...       |
        | byte[size-1]  |
        | placeholder   |
        ----------------- выровнено на границу RedZoneType
        | red_zone      | хвостовая "пограничная зона"
        ----------------
Основные идеи состоят в следующем:
1) Перед и после области данных строится зона,
   заполненная заранее известным "узором".
   Если ее содержимое изменилось, испорчено    значит мы где-то разрушили нашу память.
2) Ведется таблица всех отведенных malloc()-ом сегментов памяти;
   для экономии места эта таблица вынесена в файл (но зато это
   очень медленно).
3) Мы не можем пользоваться библиотекой STDIO для обменов с файлом,
   потому что эта библиотека сама использует malloc() и буфера
   могут быть разрушены.
*/

typedef char *RedZoneType;      /* выравнивание на границу указателя */
/* Можно выравнивать на границу double:
typedef double RedZoneType;
 */

/* Сегмент, выделяемый в оперативной памяти */
typedef struct _allocFrame {
        RedZoneType red_zone;   /* головная "пограничная зона"            */
        RedZoneType stuff[1];   /* место для данных                       */
                                /* хвостовая "пограничная зона" безымянна */
} AllocFrame;

const int RedZoneTypeSize = sizeof(RedZoneType);

/* Запись, помещаемая в таблицу всех выделенных malloc()ом
 * областей памяти.
 */
typedef struct _memFileRecord {
        AllocFrame *ptr;        /* адрес                                 */
        size_t size, adjsize;   /* размер выделенной области             */
                                /* (0,0) - означает "сегмент освобожден" */
        int serial;
} MemFileRecord;

char red_table[] = {
        0x01, 0x03, 0x02, 0x04,
        0x11, 0x13, 0x12, 0x14,
        0x21, 0x23, 0x22, 0x24,
        0x31, 0x33, 0x32, 0x34
};
char free_table[] = {
        'F', 'r', 'e', 'e', 'p', 't', 'r', '\0',
        'F', 'r', 'e', 'e', 'p', 't', 'r', '\0'
};

/* Файл для хранения таблицы указателей */
static  int mem_fd = (-1);
#define PTABLE "PointerTable.bin"

#define NRECORDS 256
MemFileRecord memrecords[NRECORDS];
/* ============================================================= */
void  MEMputTableRecord(AllocFrame *newptr, AllocFrame *oldptr,
                        size_t size, size_t adjsize);
void  MEMputTableRecordKilled(AllocFrame *ptr);
void  MEMerasePreviousRecords(AllocFrame *ptr);
int   MEMcheckRecord(MemFileRecord *rec);
int   MEMcheck_consistency(AllocFrame *ptr);
void  MEMmakeRedZones(char *cptr, size_t size, size_t adjsize);
void  MEMopenFd();
/* ============================================================= */
/* Этим следует пользоваться вместо стандартных функций          */
void *MEMmalloc (size_t size);
void *MEMrealloc(void *ptr, size_t size);
void *MEMcalloc (size_t n,  size_t size);
void  MEMfree   (void *ptr);

void  MEMcheckAll();  /* это можно вызывать в середине программы */
/* ============================================================= */
void MEMopenFd(){
        if(mem_fd < 0){
                close(creat(PTABLE, 0644));     /* создать файл */
                mem_fd = open(PTABLE, O_RDWR);  /* чтение+запись */
                unlink(PTABLE);                 /* только для M_UNIX */

                atexit(MEMcheckAll);
                setlocale(LC_ALL, "");
        }
}

/* Поместить запись в таблицу всех указателей на
 * выделенные области памяти.
 */
void MEMputTableRecord(AllocFrame *newptr, /* для записи */
                       AllocFrame *oldptr, /* для стирания */
                       size_t size,        /* размер данных */
                       size_t adjsize      /* размер всей записи с зонами */
){
        MemFileRecord memrecord;
        static int serial = 0;

        memrecord.ptr     = newptr;
        memrecord.size    = size;
        memrecord.adjsize = adjsize;
        memrecord.serial  = serial++;

        MEMopenFd();
#ifdef CHECKALL
        /* стереть прежние записи про этот адрес */
        MEMerasePreviousRecords(oldptr);
#endif
        lseek(mem_fd, 0L, SEEK_END);                    /* в конец */
        write(mem_fd, &memrecord, sizeof memrecord);    /* добавить */
}

/* Сделать запись об уничтожении области памяти */
void  MEMputTableRecordKilled(AllocFrame *ptr){
        /* Пометить как size=0, adjsize=0 */
        MEMputTableRecord(ptr, ptr, 0, 0);
}

/* Коды ответа функции проверки */
#define OK      0       /* все хорошо                 */
#define DAMAGED 1       /* повреждена "погранзона"    */
#define FREED   2       /* эта память уже освобождена */
#define NOTHERE (-1)    /* нет в таблице              */

/* Проверить сохранность "пограничных зон" */
int MEMcheckRecord(MemFileRecord *rec){
        int code = OK;
        char *cptr;
        register i;
        AllocFrame *ptr        = rec->ptr;
        size_t size            = rec->size;
        size_t adjsize         = rec->adjsize;

        if(size == 0 && adjsize == 0){
                printf("%p [%p] -- сегмент уже освобожден, "
                       "record=#%d.\n",
                        &ptr->stuff[0], ptr,
                        rec->serial
                );
                return FREED;
        }
        cptr    = (char *) ptr;
        for(i=0; i < adjsize; i++){
            if(i <  RedZoneTypeSize || i >= RedZoneTypeSize + size ){
                /* головная погранзона ИЛИ хвостовая погранзона */
                if( cptr[i] != red_table[ i % RedZoneTypeSize ] ){
                   printf("%p [%p] -- испорчен байт %4d [%4d]"
                          "= 0x%02X '%c' record=#%d size=%lu.\n",
                           &ptr->stuff[0], ptr,
                           i - RedZoneTypeSize, i,
                           cptr[i] & 0xFF,
                           isprint(cptr[i] & 0xFF) ? cptr[i] & 0xFF : '?',
                           rec->serial, size
                   );
                   code = DAMAGED;
                }
            }
        }
        for(i=0; i < RedZoneTypeSize; i++)
                if(cptr[i] == free_table[i]){
                        printf("%p -- уже освобождено?\n", ptr);
                        code = FREED;
                }
        if(code != OK) putchar('\n');
        return code;
}

/* Проверить сохранность памяти по указателю ptr. */
int MEMcheck_consistency(AllocFrame *ptr){
        MemFileRecord mr_found;
        int nrecords, i, found = 0;
        size_t size;

        MEMopenFd();

        /* Ищем запись в таблице указателей */
        lseek(mem_fd, 0L, SEEK_SET);    /* перемотать в начало */
        for(;;){
                size = read(mem_fd, memrecords, sizeof memrecords);
                nrecords = size / sizeof(memrecords[0]);

                if(nrecords <= 0) break;

                for(i=0; i < nrecords; i++)
                        if(memrecords[i].ptr == ptr){
                        /* Мы ищем последнюю запись про память
                         * с таким адресом, поэтому
                         * вынуждены прочитать ВЕСЬ файл.
                         */
                                mr_found = memrecords[i];
                                found++;
                        }
        }
        if(found) {
                return MEMcheckRecord(&mr_found);
        } else {
                printf("%p -- запись в таблице отсутствует.\n", ptr);
                return NOTHERE;
        }
}

/* Уничтожить все прежние записи про ptr, прописывая их adjsize=0 */
void MEMerasePreviousRecords(AllocFrame *ptr){
        int nrecords, i, found;
        size_t size;

        MEMopenFd();
        lseek(mem_fd, 0L, SEEK_SET);    /* перемотать в начало */
        for(;;){
                found = 0;
                size = read(mem_fd, memrecords, sizeof memrecords);
                nrecords = size / sizeof(memrecords[0]);

                if(nrecords <= 0) break;

                for(i=0; i < nrecords; i++)
                        if(memrecords[i].ptr == ptr){
                                memrecords[i].adjsize = 0;
                                /* memrecords[i].size = 0; */
                                found++;
                        }
                if(found){
                        lseek(mem_fd, -size, SEEK_CUR);    /* шаг назад */
                        write(mem_fd, memrecords, size);   /* перезаписать */
                }
        }
}

void MEMcheckAll(){
#ifdef CHECKALL
        int nrecords, i;
        size_t size;

        printf("Проверка всех указателей -------------\n");
        MEMopenFd();
        lseek(mem_fd, 0L, SEEK_SET);    /* перемотать в начало */
        for(;;){
                size = read(mem_fd, memrecords, sizeof memrecords);
                nrecords = size / sizeof(memrecords[0]);

                if(nrecords <= 0) break;

                for(i=0; i < nrecords; i++)
                        if(memrecords[i].adjsize != 0)
                                MEMcheckRecord(&memrecords[i]);
        }
        printf("Проверка всех указателей завершена ---\n");
#endif
}

/* ============================================================= */
/* Заполнение пограничных зон образцом - "следовой дорожкой" */
void MEMmakeRedZones(char *cptr, size_t size, size_t adjsize){
        register i;

        for(i=0; i < adjsize; i++){
                if(i <  RedZoneTypeSize || i >= RedZoneTypeSize + size ){
                   /* головная погранзона ИЛИ
                    * хвостовая погранзона + дополнение
                    * до целого числа RedZoneType-ов
                    */
                        cptr[i] = red_table[ i % RedZoneTypeSize ];
                }
        }
}
/* ============================================================= */
/* Функция выделения памяти */
void *MEMmalloc(size_t size){
        AllocFrame *retptr;
        int fullRedZoneTypes =
                (size + RedZoneTypeSize - 1) / RedZoneTypeSize;
        size_t adjustedSize =
                sizeof(retptr->red_zone) * 2 + /* две погранзоны */
                fullRedZoneTypes * RedZoneTypeSize;

        retptr  = (AllocFrame *) malloc(adjustedSize);
        if(retptr == NULL) return NULL;

        MEMmakeRedZones ((char *) retptr, size, adjustedSize);
        MEMputTableRecord(retptr, retptr, size, adjustedSize);
        return &retptr->stuff[0];
        /* вернуть указатель на зону данных */
}

void *MEMrealloc(void *ptr, size_t size){
        AllocFrame *retptr;
        char *cptr = (char *)ptr - RedZoneTypeSize;  /* прежний AllocFrame */
        AllocFrame *oldptr = (AllocFrame *) cptr;
        int fullRedZoneTypes =
                (size + RedZoneTypeSize - 1) / RedZoneTypeSize;
        size_t adjustedSize =
                sizeof(retptr->red_zone) * 2 +
                fullRedZoneTypes * RedZoneTypeSize;

        /* Проверить сохранность того, что мы сейчас будем realloc-ить */
        MEMcheck_consistency(oldptr);

        retptr  = (AllocFrame *) realloc((void *)oldptr, adjustedSize);
        if(retptr == NULL) return NULL;

        MEMmakeRedZones ((char *) retptr, size, adjustedSize);
        MEMputTableRecord(retptr, oldptr, size, adjustedSize);
        return &retptr->stuff[0];
}

void *MEMcalloc(size_t n, size_t size){
        size_t newsize = n * size;
        void *ptr = MEMmalloc(newsize);
        memset(ptr, '\0', newsize);
        return ptr;
}

/* Очистка отведенной памяти.
 * ptr - это указатель не на AllocFrame,
 * а на данные - то есть на stuff[0].
 */
void MEMfree(void *ptr){
        char *cptr = (char *)ptr - RedZoneTypeSize;
        int i, code;

        code = MEMcheck_consistency((AllocFrame *) cptr);
        for(i=0; i < RedZoneTypeSize; i++)
                cptr[i] = free_table[i];

        if(code != FREED) free((void *) cptr);

        MEMputTableRecordKilled((AllocFrame *) cptr);
}

/* ============================================================= */
/* Тестовый пример                                               */
/* ============================================================= */
#define MAXPTRS 512
char *testtable[MAXPTRS];

/* Сгенерировать строку случайной длины со случайным содержимым */
char *wildstring(int c){
#define N 1024
        char teststring[N + 1];
        int len, i;
        char *ptr;

        len = rand() % N;
        for(i=0; i < len; i++)
                teststring[i] = c;
        teststring[len] = '\0';

        ptr = (char *) MEMmalloc(len + 1);
        if(ptr){
                strcpy(ptr, teststring);
        } else printf("NULL wildstring()\n");

        return ptr;
}

int main(int ac, char *av[]){
        int ilen, len, n, i;

        srand(time(NULL));

        for(n=0; n < MAXPTRS; n++)
                testtable[n] = wildstring('A');

#define DAMAGE (MAXPTRS/3*2-1)
#ifdef DAMAGE
        /* Навести порчу */
        len = strlen(testtable[DAMAGE]);
        testtable[DAMAGE][len+1] = 'x';
        testtable[DAMAGE][-2]    = 'y';
        printf("ptr=%p len=%d\n", testtable[DAMAGE], len);
#endif
        for(n=0; n < MAXPTRS/2; n++){
                char *p = wildstring('B');
                int length = strlen(p);
                char *ptr;

                i = rand() % MAXPTRS;
                /* Не забыть присвоить возвращенное realloc() значение
                 * обратно в testtable[i] !!!
                 */
                testtable[i] = ptr =
                        (char *) MEMrealloc(testtable[i], length + 1);

                if(ptr == NULL) printf("Не могу сделать realloc()\n");
                else            strcpy(ptr, p);

#ifdef DAMAGE
                /* Порча */
                if(n == MAXPTRS/3){
                        ptr[length+2] = 'z';
                }
#endif
                MEMfree(p);
        }

        for(n=0; n < MAXPTRS; n++){
                if(testtable[n]) MEMfree(testtable[n]);
        }
#ifdef DAMAGE
        MEMfree(testtable[DAMAGE]);
#endif
        return 0;
}

© Copyright А. Богатырев, 1992-95 www.abyss-group.narod.ru
Си в UNIX

Назад | Содержание | Вперед

Hosted by uCoz