Пример 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
Назад | Содержание | Вперед