xref: /reactos/base/shell/cmd/cmddbg.c (revision 8a978a17)
1 #include "precomp.h"
2 
3 #ifdef _DEBUG_MEM
4 
5 #define REDZONE_SIZE  32
6 #define REDZONE_LEFT 0x78
7 #define REDZONE_RIGHT 0x87
8 
9 typedef struct
10 {
11     size_t size;
12     LIST_ENTRY list_entry;
13     const char *file;
14     int line;
15 } alloc_info, *palloc_info;
16 
17 static size_t allocations = 0;
18 static size_t allocated_memory = 0;
19 static LIST_ENTRY alloc_list_head = {&alloc_list_head, &alloc_list_head};
20 
21 static void *
22 get_base_ptr(void *ptr)
23 {
24     return (void *)((UINT_PTR)ptr - REDZONE_SIZE - sizeof(alloc_info));
25 }
26 
27 static void *
28 get_ptr_from_base(palloc_info info)
29 {
30     return (void*)((size_t)(info + 1) + REDZONE_SIZE);
31 }
32 
33 static void *
34 write_redzone(void *ptr, size_t size, const char *file, int line)
35 {
36     void *ret;
37     palloc_info info = (palloc_info)ptr;
38 
39     info->size = size;
40     info->file = file;
41     info->line = line;
42 
43     ptr = (void *)(info + 1);
44     memset(ptr, REDZONE_LEFT, REDZONE_SIZE);
45     ret = (void *)((size_t)ptr + REDZONE_SIZE);
46     ptr = (void *)((size_t)ret + size);
47     memset(ptr, REDZONE_RIGHT, REDZONE_SIZE);
48     return ret;
49 }
50 
51 static int
52 check_redzone_region(void *ptr, unsigned char sig, void **newptr)
53 {
54     unsigned char *p, *q;
55     int ret = 1;
56 
57     p = (unsigned char *)ptr;
58     q = p + REDZONE_SIZE;
59     while (p != q)
60     {
61         if (*(p++) != sig)
62             ret = 0;
63     }
64 
65     if (newptr != NULL)
66         *newptr = p;
67     return ret;
68 }
69 
70 static void
71 redzone_err(const char *msg, palloc_info info, void *ptr, const char *file, int line)
72 {
73     DbgPrint("CMD: %s\n", msg);
74     DbgPrint("     Block: 0x%p Size: %lu\n", ptr, info->size);
75     DbgPrint("     Allocated from %s:%d\n", info->file, info->line);
76     DbgPrint("     Detected at: %s:%d\n", file, line);
77     ASSERT(FALSE);
78     ExitProcess(1);
79 }
80 
81 static void
82 check_redzone(void *ptr, const char *file, int line)
83 {
84     palloc_info info = (palloc_info)ptr;
85     ptr = (void *)(info + 1);
86     if (!check_redzone_region(ptr, REDZONE_LEFT, &ptr))
87         redzone_err("Detected buffer underflow!", info, ptr, file, line);
88     ptr = (void *)((UINT_PTR)ptr + info->size);
89     if (!check_redzone_region(ptr, REDZONE_RIGHT, NULL))
90         redzone_err("Detected buffer overflow!", info, ptr, file, line);
91 }
92 
93 static size_t
94 calculate_size_with_redzone(size_t size)
95 {
96     return sizeof(alloc_info) + size + (2 * REDZONE_SIZE);
97 }
98 
99 static void
100 add_mem_to_list(void *ptr)
101 {
102     palloc_info info = (palloc_info)ptr;
103     InsertTailList(&alloc_list_head, &info->list_entry);
104 }
105 
106 static void
107 del_mem_from_list(void *ptr)
108 {
109     palloc_info info = (palloc_info)ptr;
110     RemoveEntryList(&info->list_entry);
111 }
112 
113 static void
114 dump_mem_list(void)
115 {
116     palloc_info info;
117     PLIST_ENTRY entry;
118     void *ptr;
119 
120     entry = alloc_list_head.Flink;
121     while (entry != &alloc_list_head)
122     {
123         info = CONTAINING_RECORD(entry, alloc_info, list_entry);
124 
125         DbgPrint(" * Block: 0x%p Size: %lu allocated from %s:%d\n", get_ptr_from_base(info), info->size, info->file, info->line);
126 
127         ptr = (void *)(info + 1);
128         if (!check_redzone_region(ptr, REDZONE_LEFT, &ptr))
129         {
130             DbgPrint("   !!! Detected buffer underflow !!!\n");
131         }
132 
133         ptr = (void *)((UINT_PTR)ptr + info->size);
134         if (!check_redzone_region(ptr, REDZONE_RIGHT, NULL))
135         {
136             DbgPrint("   !!! Detected buffer overflow !!!\n");
137         }
138 
139         entry = entry->Flink;
140     }
141 }
142 
143 void *
144 cmd_alloc_dbg(size_t size, const char *file, int line)
145 {
146     void *newptr = NULL;
147 
148     newptr = malloc(calculate_size_with_redzone(size));
149     if (newptr != NULL)
150     {
151         allocations++;
152         allocated_memory += size;
153         add_mem_to_list(newptr);
154         newptr = write_redzone(newptr, size, file, line);
155     }
156 
157     return newptr;
158 }
159 
160 void *
161 cmd_realloc_dbg(void *ptr, size_t size, const char *file, int line)
162 {
163     size_t prev_size;
164     void *newptr = NULL;
165 
166     if (ptr == NULL)
167         return cmd_alloc_dbg(size, file, line);
168     if (size == 0)
169     {
170         cmd_free_dbg(ptr, file, line);
171         return NULL;
172     }
173 
174     ptr = get_base_ptr(ptr);
175     prev_size = ((palloc_info)ptr)->size;
176     check_redzone(ptr, file, line);
177 
178     del_mem_from_list(ptr);
179     newptr = realloc(ptr, calculate_size_with_redzone(size));
180     if (newptr != NULL)
181     {
182         allocated_memory += size - prev_size;
183         add_mem_to_list(newptr);
184         newptr = write_redzone(newptr, size, file, line);
185     }
186     else
187         add_mem_to_list(ptr);
188 
189     return newptr;
190 }
191 
192 void
193 cmd_free_dbg(void *ptr, const char *file, int line)
194 {
195     if (ptr != NULL)
196     {
197         ptr = get_base_ptr(ptr);
198         check_redzone(ptr, file, line);
199         allocations--;
200         allocated_memory -= ((palloc_info)ptr)->size;
201         del_mem_from_list(ptr);
202     }
203 
204     free(ptr);
205 }
206 
207 TCHAR *
208 cmd_dup_dbg(const TCHAR *str, const char *file, int line)
209 {
210     TCHAR *ptr = NULL;
211 
212     if (str != NULL)
213     {
214         ptr = (TCHAR *)cmd_alloc_dbg((_tcslen(str) + 1) * sizeof(TCHAR), file, line);
215         if (ptr != NULL)
216         {
217             _tcscpy(ptr, str);
218         }
219     }
220 
221     return ptr;
222 }
223 
224 void
225 cmd_checkbuffer_dbg(void *ptr, const char *file, int line)
226 {
227     if (ptr != NULL)
228     {
229         ptr = get_base_ptr(ptr);
230         check_redzone(ptr, file, line);
231     }
232 }
233 
234 void
235 cmd_exit(int code)
236 {
237     if (allocations != 0 || allocated_memory != 0)
238     {
239         DbgPrint("CMD: Leaking %lu bytes of memory in %lu blocks! Exit code: %d\n", allocated_memory, allocations, code);
240         if (allocations != 0)
241             dump_mem_list();
242     }
243 
244     ExitProcess(code);
245 }
246 
247 #endif /* _DEBUG_MEM */
248