Dynamic memory allocation and management is common in application programming.

We use malloc()/calloc() for memory allocation and use free() for deallocation.

In normal situations we use memory in blocks. We never access or modify beyond the range or boundary. But sometimes it may happen that we access array elements beyond the array limit. These situations create memory corruption.

Memory corruption creates many problems. It can set unpredictable values to next memory elements; it can set invalid pointer values; and worst of all, it can corrupt dynamic memory allocator thus causing crash/core-dump of the application/process. Memory corruption may occur because of poor array buffer handling or some abnormal runtime use-cases.

Here we will discuss some aspects of checking integrity of memory chunks.

This program allocates memory to the nodes of a linked list of student records. Student data-structure has id, name and age as member variable. It accepts name and age from user and sets these values to the nodes.

In the normal run, we usually give small names like Student1, Student2 etc. As a result this program does not cause any memory corruption.

Here we are assuming that name should be less than 20 char long. But user may give very long names and we are not taking precautions to avoid such situations. Thus when user gives a name longer than 20 char, it creates memory corruption.

Now how to check this corruption?

Here we are allocating some extra memory at the beginning and at the end of each memory unit. After allocation we are applying some character pattern to this extra memory.

Why we are doing this?

This helps us to detect the corruption. When anyone writes beyond the allocated memory blocks, these character patterns will be modified and we can easily detect the corruption. Please note that this memory corruption checker is very tricky and may not work in all situations.

Allocated blocks will look like this-

"XYXYXYXY<- BUFFER 1->XYXYXYXY""XYXYXYXY<- BUFFER 2->XYXYXYXY"

When buffer2 will overflow, it will corrupt/overwrite trailing pattern and memory block will look like

"XYXYXYXY<- BUFFER 1->XYXYXYXY""XYXYXYXY<- BUFFER 2 ->XYXY"

Functions:

  • mem_alloc() - This allocates memory using malloc() with 16 extra bytes. Then it sets 8 byte pattern at the beginning and end of the buffer. It returns the buffer to the user. It uses m_ctx array to save this info to track all memory allocation from user.
  • mem_free()-This deallocates memory and sets the m_ctx object as not allocated context.
  • mem_check()- We are tracking all memory allocation with m_ctx array thus this function iterates all memory blocks and checks the beginning and end pattern of each memory buffer. If one or more pattern is not correct, it means that the memory block has some corruption.
To simulate memory corruption at runtime we must give a long name of the student. See that we are giving a long name to the third student and this is getting checked in mem_check()

Program output:

Add a new student record(y/n) ?y

Name:student1

Age:12

Add a new student record(y/n) ?y

Name:student2

Age:13

Add a new student record(y/n) ?y

Name:ABCDEFGHIJKLMNOPQRSTUVWXYZ

Age:12

Address 0x0x91fc0d0, allocated at mem_chk.c:120:main() is corrupted.

Add a new student record(y/n) ?

Source Code:


#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<memory.h>

#define MAX_NODES 1000
const char * pattern = "XYXYXYXY";

typedef struct mem_node {
  void * addr;
  int size;
  int line;
  const char * file;
  const char *function;
} mem_node_t;

mem_node_t m_ctx[MAX_NODES];

void * mem_alloc(int size,
                 const char* file,
                 int line,
                 const char * function)
{
  int i;
  char * addr = NULL;

  if (size <= 0) {
    return NULL;
  }

  for (i = 0; i < MAX_NODES; i++) {

    if(NULL == (char*)(m_ctx[i].addr) ) {

    addr = (char *)malloc(size + 16);
    if ( addr == NULL) {
      exit(-1);
    }

    memcpy(addr, pattern, 8);
    memcpy(addr + size + 8, pattern, 8);
    m_ctx[i].addr = addr;
    m_ctx[i].size = size;
    m_ctx[i].line = line;
    m_ctx[i].file = file;
    m_ctx[i].function = function;
    break;
    }
  }
  return (addr + 8);
}

void * mem_free(void *addr)
{
  int i;
  char * addr_c = (char*)addr;

  if (addr == NULL) {
    return;
  }
  
  addr_c -= 8;
  for (i = 0; i < MAX_NODES; i++) {

    if(addr_c == (char*)(m_ctx[i].addr)) {

      free(addr_c);
      m_ctx[i].addr = NULL;
      m_ctx[i].size = 0;
      m_ctx[i].line = -1;
      m_ctx[i].file = NULL;
      m_ctx[i].function = NULL;
    }
  }
}

void mem_check(void)
{

  int i;
  char * addr = NULL;

  for (i = 0; i < MAX_NODES; i++) {

    if(NULL != (char*)(m_ctx[i].addr)) {
    addr = m_ctx[i].addr;

      if (memcmp(addr, pattern, 8) != 0 ||
        memcmp(addr + m_ctx[i].size + 8, pattern, 8) != 0) {
        printf("Address %p, allocated at %s:%d:%s() is corrupted.\n",
               m_ctx[i].addr,
               m_ctx[i].file,
               m_ctx[i].line,
               m_ctx[i].function);
      }
    }
  }
}

#undef malloc
#undef free
#define malloc(size) mem_alloc(size, __FILE__, __LINE__, __FUNCTION__)
#define free(x) mem_free(x)

/*
 This is the application code which can create memory corruption
*/

typedef struct student {

 int id;
 char *name;
 int age;
 struct student *next;

} student_t;

int main(int argc, char*argv[])
{
  student_t * std_p = NULL, *tmp_p;
  int s_id = 0;
  char *name = NULL;
  char c;
  char input[10];
  do {
    printf("Add a new student record(y/n) ?");
    fflush(stdin);
    scanf("%s", &input[0]);

    if (strcmp(input, "y") != 0) {
      break;
    }
    tmp_p = (student_t *)malloc(sizeof(student_t));
    name = (char *)malloc(20);

    if (tmp_p == NULL || name == NULL) {
      exit(-1);
    }
    printf("Name:");
    fflush(stdin);
    scanf("%s", &name[0]);
    printf("Age:");
    scanf("%d", &tmp_p->age);
    tmp_p->id = ++s_id;
    tmp_p->next = NULL;
    tmp_p->name = name;

    if (std_p == NULL) {
      std_p = tmp_p;
    } else {
      std_p->next = tmp_p;
    }
    
    mem_check();

  } while (1);

  return 0;
}

You have viewed 1 page out of 248. Your C learning is 0.00% complete. Login to check your learning progress.

 Vote 0

Similar topics related to this section

C exception handling, setjmp and longjmp, trace a memory leak, detect memory corruption, Big Endian and Little Endian, stack grows up or down,

# C Programming Language (Prentice Hall Software)
# Let Us C Paperback - 2006 by Yashavant Kanetkar
# Understanding and Using C Pointers Core techniques for memory management
# Data Structures Using C and C++ Paperback - 1998
# Data Structures In C Paperback - August 11, 2008 by Noel Kalicharan