Memory corruption in embedded devices refers to the unintentional alteration of data stored in a device’s memory in a way, that can cause the device to malfunction or behave unexpectedly. Memory corruption can occur in volatile memory such as RAM as well as on Non-volatile memory such as EEPROM/Flash memory. In this article we will discuss different types of memory corruption. Although there are several types of memory corruption that can occur in embedded devices, some of them are:
As we know that there are 5 memory segments (i.e. text segment, bss segment, initialized data segment, Stack and heap). You can read more on this link. The stack is a region of memory that is used to store local variables and function arguments when a program is executing. It is organized in a Last In First Out (LIFO) manner, meaning that the most recently added item is the first one to be removed.
When a program calls a function, the function’s arguments and local variables are stored on the stack. If the program writes more data to a buffer on the stack than the stack can hold, the excess data will overwrite adjacent memory locations on the stack. This can corrupt the values of other variables and disrupt the normal operation of the program.
An attacker can exploit a stack-based buffer overflow vulnerability to execute malicious code on a device by injecting code into the stack and tricking the program into executing it. This can allow the attacker to gain unauthorized access to the device or to perform actions on the device that are not intended by the device’s designers.
To prevent stack-based buffer overflows, it is important for programmers to properly validate and sanitize user input and to ensure that buffers are large enough to hold the expected amount of data.
You can also read “How to detect stack overflow …” to understand it more.
The heap is a region of memory that is used to store dynamically allocated variables. It is not as strictly organized as the stack, and the memory locations on the heap are not necessarily contiguous. If the program writes at the address that is not in range of allocated memory, then it will cause memory corruption. This corruption could be within the heap area or may be outside the heap area.
- Dangling pointer i.e. Use-after-free
Dangling pointer corruption can occur in an embedded device when a program attempts to access memory that has been freed, but has not yet been reallocated. When a program no longer needs a block of dynamically allocated memory, it can free the memory by calling the
free() function. If a program attempts to access memory that has been freed but has not yet been reallocated, it may be accessed by another program or by the operating system. This can cause the program to behave unexpectedly or to crash.
To prevent dangling pointer corruption, it is important for programmers to properly manage dynamically allocated memory by ensuring that pointers are only used to access memory that is still allocated and by properly deallocating memory when it is no longer needed
- Integer Overflow Corruption
Integer overflow is a type of memory corruption that occurs when an integer value is incremented beyond the maximum value that can be stored in a variable of that type. This can result in the value wrapping around to a negative number, which can cause the program to behave unexpectedly.
Integer overflow can occur when a program performs arithmetic operations on integers and the result exceeds the maximum value that can be stored in the integer type. For example, if an
int variable has a maximum value of 2147483647, and the program adds 1 to it, the result will be -2147483648.
To prevent integer overflow vulnerabilities, it is important for programmers to properly validate and sanitize user input and to ensure that integer values do not exceed the maximum value that can be stored in the variable. It is also a good practice to use larger integer types, such as
long long, when working with large integer values to reduce the risk of overflow.
- Uninitialized memory use corruption
Uninitialized memory use is a type of memory corruption that occurs when a program uses memory that has not been properly initialized. This can cause the program to behave unexpectedly or crash.
In C, variables must be initialized before they can be used. If a program attempts to use an uninitialized variable, it may contain an arbitrary value that has been left over from previous usage of the memory. This can lead to unpredictable behavior and can be exploited by attackers to gain unauthorized access to a device or to execute malicious code on the device.
To prevent uninitialized memory use vulnerabilities, it is important for programmers to properly initialize variables before using them. This can be done by assigning a value to the variable or by using the
memset() function to set all the bytes in the memory to a specific value. It is also a good practice to use the
valgrind tool to detect uninitialized memory use during testing.