Here is one of the erroneous C program I tested my test tool with. The C program reads uninitialized variable both in
process
and process2
functions from the CONTEXT
structure. Highlighted the lines accessing uninitialized memory.// TestRBW.cpp : Example file to test read-before-write bugs.And here is the result of the test. In the log you can see both of the bugs were detected.
// Compile with optimization disabled.
#include "stdafx.h"
#include <string.h>
#define MAX 16
typedef struct _CONTEXT
{
int arr[MAX];
int a;
int b;
int c;
} CONTEXT;
void init(CONTEXT* ctx)
{
memset(ctx->arr, 0, sizeof(ctx->arr[0]) * (MAX-1));
ctx->a = 1;
}
void process(CONTEXT* ctx)
{
int trash;
for (int i = 0; i < MAX; i++)
{
trash = ctx->arr[i];
}
}
void process2(CONTEXT* ctx)
{
ctx->b = ctx->c;
}
void process3(int num)
{
int trash;
if (num)
trash = num;
}
int _tmain(int argc, _TCHAR* argv[])
{
CONTEXT ctx;
// Erroneously initializes context. The last element of arr member remains unitialized.
// b and c members remain uninitialized, too.
init(&ctx);
// Accesses to each element of the array. Read-before-write error should be reported in this function.
process(&ctx);
// Copies c to b but c is uninitialized. Read-before-write error should be reported in this function.
process2(&ctx);
// This contains no read-before-write bug.
process3(ctx.a);
}
0:000> !region 18 18f000 1000Looking the functions of the two EIPs. Highlighted the lines accessing uninitialized memory.
[Data Access] Data at 0018ff28 is read before it is written
[Data Access] EIP=00401044 Data=0018ff28 R
[Data Access] 00401044 8b048a mov eax,dword ptr [edx+ecx*4]
[Data Access] Data at 0018ff34 is read before it is written
[Data Access] EIP=00401059 Data=0018ff34 R
[Data Access] 00401059 8b5148 mov edx,dword ptr [ecx+48h]
Break reason: 00000010
TestRBW!process:
00401020 55 push ebp
00401021 8bec mov ebp,esp
00401023 83ec08 sub esp,8
00401026 c745f800000000 mov dword ptr [ebp-8],0
0040102d eb09 jmp TestRBW!process+0x18 (00401038)
0040102f 8b45f8 mov eax,dword ptr [ebp-8]
00401032 83c001 add eax,1
00401035 8945f8 mov dword ptr [ebp-8],eax
00401038 837df810 cmp dword ptr [ebp-8],10h
0040103c 7d0e jge TestRBW!process+0x2c (0040104c)
0040103e 8b4df8 mov ecx,dword ptr [ebp-8]
00401041 8b5508 mov edx,dword ptr [ebp+8]
00401044 8b048a mov eax,dword ptr [edx+ecx*4]
00401047 8945fc mov dword ptr [ebp-4],eax
0040104a ebe3 jmp TestRBW!process+0xf (0040102f)
0040104c 8be5 mov esp,ebp
0040104e 5d pop ebp
0040104f c3 ret
TestRBW!process2:I built this new functionality on the top of one mentioned in the previous post. The additions are the followings:
00401050 55 push ebp
00401051 8bec mov ebp,esp
00401053 8b4508 mov eax,dword ptr [ebp+8]
00401056 8b4d08 mov ecx,dword ptr [ebp+8]
00401059 8b5148 mov edx,dword ptr [ecx+48h]
0040105c 895044 mov dword ptr [eax+44h],edx
0040105f 5d pop ebp
00401060 c3 ret
- We trace both read and write memory accesses to the stack memory.
- We maintain a structure to flag what memory addresses on the stack have been written.
- Before the program starts to trace the memory it considers that memory addresses greater or equal than ESP have been written and maintain the structure according to this.
- If a write memory access occurs we maintain the structure to flag the memory region has been written.
- If an element is popped from the stack (read access to stack memory) we maintain the structure to remove addresses belongs to the unused portion of the stack memory.
- If a read access memory occurs we read the structure and check if the memory at the address has been written. Giving notification according to results.