Memory corruption

Memory corruption is a process of unintentionally alterting a random area of memory by a program or process. A program written with C programming language can corrupt memory in different areas and in different ways. Memory Memory corruption creates many problems and program execution faces many abnormal behavior. 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. It can corrupt stack and alter the return address or stack pointer. Program can hang or crash with core-dump. Pointer variable can point to a invalid memory location which can cause access violation and a crash. Memory corruption may occur because of poor array buffer handling or some abnormal runtime use-cases.

Global variable corruption (array out of bound access)

Here we have two variables in global scope. Second variable is located after the boundary of first array. This first arrary has 10 elements so the lower bound is 0 and upper bound is 9. For loop should be in the boundary between 0 to 9. A bad programming logic can access a position which will be beyond the boundary of first array. Poorly written for loop access 11th element (10th offset in C).

#include <stdio.h>
int var1[10];
int var2;
int main (int argc, char * argv[])
{
  int i;
  var2 = 0;
  printf ("Var 2 = %d ", var2);
  for (= 0; i <= 10; i++)
  {
    var1[i] = i;
  }
  for (= 0; i <= 10; i++)
  {
    printf ("Var1[%d] = %d ", i, var1[i]);
  }
  printf ("Var 2 = %d ", var2);
  return 0;  
}
$ ./a.out 
Var 2 = 0
Var1[0] = 0
Var1[1] = 1
Var1[2] = 2
Var1[3] = 3
Var1[4] = 4
Var1[5] = 5
Var1[6] = 6
Var1[7] = 7
Var1[8] = 8
Var1[9] = 9
Var1[10] = 10
Var 2 = 10
$ 

Global variable corruption (using strings)

Here we have two variables in global scope. First one is a string i.e. array of char and there is a integer after that. The string functions should be used in such a way that it should stay in the boundary of 10 bytes. Here gets function has been used which is harmfull if user enters any string beyond 10 characters. This below program work fine when user enters a string of length 9 or below. A string beynd 9 or more should corrupt var2.

#include <stdio.h>
char var1[10];
int var2;
int main (int argc, char * argv[])
{
  int i;
  var2 = 0;
  printf ("Var 2 = %d ", var2);
  printf ("Enter a string : ", var2);
  gets(var1);
  
  printf ("Var 2 = %d ", var2);
  return 0;  
}
$ ./a.out 
Var 2 = 0
warning: this program uses gets(), which is unsafe.
Enter a string : I am corrupting global variable.
Var 2 = 543649385
$ 

Local variable corruption (using strings)

We are now seeing one example with local variables. Local variables are placed in program stack. Stack grows in opposite direction in the memory. Here we have taken a variable var2 of type integer and then there is a character var1. We are using strcpy to copy a string to var1. It is a character so copying a string will corrupt the next located integer var2.

#include <stdio.h>
int main (int argc, char * argv[])
{
  int i;
  
  int var2;   
  char var1;
  var2 = 0;
  printf ("Var 2 = %d ", var2);
  strcpy(&var1, "Hello world!");
  printf ("Var 2 = %d ", var2);
  return 0;  
}
Var 2 = 0
Var 2 = 1869376613

Heap variable corruption (using strings)

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.

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.

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

Guard buffer with signature

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.

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"

memory corruption

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()

Detect corruption - 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) ?

Detect corruption - 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 (= 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 (= 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 (= 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. ",
               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;
}

About our authors: Team EQA

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

Further readings

Construct your own strcpy and strcat/implementation of strcpy, strcat function in C
strcpy, strcat implementation in c using pointers, strncpy, strcat implementation in c, strcmp, strcat implementation in c, strcat implementation in c, implementation of strcpy, strcat function in c

memmove vs memcpy how different? implement your own code?
memmove vs memcpy, memmove and memcpy difference understanding overlapping memory with diagram. Implement your own memmove memcpy. example source code

How exceptions are handled in C? (return check, exit to OS in fatal error, NULL check, setjmp-longjmp)
Describes how exceptions are handled in C. return check for success, exit to OS in case of fatal error, NULL pointer check, try-catch like exception handling with setjmp-longjmp

What is setjmp and longjmp in C? How to handle C++ like try-catch exceptions in C?
Describes C++ like try-catch exception handling in C with setjmp and longjmp. setjmp saves context and longjmp reverts context back in error with source code example

What is memory leak? How to check memory leak in c program?
memory leak in c, how to check memory leak in c program, how to avoid memory leak in c, how to debug memory leak in c, memory leak example

Learn on Youtube

#