Пример 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;

}

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

Используются технологии uCoz