Пример 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
Си в UNIX

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