aboutsummaryrefslogtreecommitdiffstats
path: root/memcheck/memcheck_common.h
blob: e753094b4a02daf93f9c9f1df68ee8982651041e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/* Copyright (C) 2007-2010 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
*/

/*
 * Contains declarations of structures, routines, etc. that are commonly used
 * in memechecker framework.
 */

#ifndef QEMU_MEMCHECK_MEMCHECK_COMMON_H
#define QEMU_MEMCHECK_MEMCHECK_COMMON_H

#include "qemu-common.h"
#include "cpu.h"

#ifdef __cplusplus
extern "C" {
#endif

// =============================================================================
// Events generated by the guest system.
// =============================================================================

/* Notifies the emulator that libc has been initialized for a process.
 * Event's value parameter is PID for the process in context of which libc has
 * been initialized.
 */
#define TRACE_DEV_REG_LIBC_INIT             1536

/* Notifies the emulator about new memory block being allocated.
 * Event's value parameter points to MallocDesc instance in the guest's address
 * space that contains allocated block information. Note that 'libc_pid' field
 * of the descriptor is used by emulator to report failure in handling this
 * event. In case of failure emulator will zero that filed before completing
 * this event.
 */
#define TRACE_DEV_REG_MALLOC                1537

/* Notifies the emulator about memory block being freed.
 * Event's value parameter points to MallocFree descriptor instance in the
 * guest's address space that contains information about block that's being
 * freed. Note that 'libc_pid' field of the descriptor is used by emulator to
 * report failure in handling this event. In case of failure emulator will zero
 * that filed before completing this event.
 */
#define TRACE_DEV_REG_FREE_PTR              1538

/* Queries the emulator about memory block information.
 * Event's value parameter points to MallocDescQuery descriptor instance in the
 * guest's address space that contains query parameters. Note that 'libc_pid'
 * field of the descriptor is used by emulator to report failure in handling
 * this event. In case of failure emulator will zero that filed before
 * completing this event.
 */
#define TRACE_DEV_REG_QUERY_MALLOC          1539

/* Queries the emulator to print a string to its stdout.
 * Event's value parameter points to zero-terminated string to be printed. Note
 * that this string is located in the guest's address space.
 */
#define TRACE_DEV_REG_PRINT_USER_STR        1540

// =============================================================================
// Communication structures
// =============================================================================

/* Describes memory block allocated from the heap. This structure is passed
 * along with TRACE_DEV_REG_MALLOC event. This descriptor is used to inform
 * the emulator about new memory block being allocated from the heap. The entire
 * structure is initialized by the guest system before event is fired up. It is
 * important to remember that same structure (an exact copy) is also declared
 * in the libc's sources. So, every time a change is made to any of these
 * two declaration, another one must be also updated accordingly. */
typedef struct MallocDesc {
    /* Poniter to the memory block actually allocated from the heap. Note that
     * this is not the pointer that is returned to the malloc's caller. Pointer
     * returned to the caller is calculated by adding value stored in this field
     * to the value stored in prefix_size field of this structure.
     */
    target_ulong    ptr;

    /* Nuber of bytes requested by the malloc's caller. */
    uint32_t        requested_bytes;

    /* Byte size of the prefix data. Actual pointer returned to the malloc's
     * caller is calculated by adding value stored in this field to the value
     * stored in in the ptr field of this structure.
     */
    uint32_t        prefix_size;

    /* Byte size of the suffix data. */
    uint32_t        suffix_size;

    /* Id of the process that initialized libc instance, in which allocation
     * has occurred. This field is used by the emulator to report errors in
     * the course of TRACE_DEV_REG_MALLOC event handling. In case of an error,
     * emulator sets this field to zero (invalid value for a process ID).
     */
    uint32_t        libc_pid;

    /* Id of the process in context of which allocation has occurred.
     * Value in this field may differ from libc_pid value, if process that
     * is doing allocation has been forked from the process that initialized
     * libc instance.
     */
    uint32_t        allocator_pid;

    /* Number of access violations detected on this allocation. */
    uint32_t        av_count;
} MallocDesc;
/* Helpers for addressing field in MallocDesc structure, using which emulator
 * reports an error back to the guest.
 */
#define ALLOC_RES_OFFSET        ((uint32_t)(ptrdiff_t)&(((MallocDesc*)0)->libc_pid))
#define ALLOC_RES_ADDRESS(p)    (p + ALLOC_RES_OFFSET)

/* Describes memory block info queried from emulator. This structure is passed
 * along with TRACE_DEV_REG_QUERY_MALLOC event. When handling free and realloc
 * calls, it is required that we have information about memory blocks that were
 * actually allocated in previous calls to malloc, memalign, or realloc. Since
 * we don't keep this information directlry in the allocated block, but rather
 * we keep it in the emulator, we need to query emulator for that information
 * with TRACE_DEV_REG_QUERY_MALLOC query. The entire structure is initialized
 * by the guest system before event is fired up It is important to remember that
 * same structure (an exact copy) is also declared in the libc's sources. So,
 * every time a change is made to any of these two declaration, another one
 * must be also updated accordingly.
 */
typedef struct MallocDescQuery {
    /* Pointer for which information is queried. Note that this pointer doesn't
     * have to be exact pointer returned to malloc's caller, but can point
     * anywhere inside an allocated block, including guarding areas. Emulator
     * will respond with information about allocated block that contains this
     * pointer.
     */
    target_ulong    ptr;

    /* Id of the process that initialized libc instance, in which this query
     * is called. This field is used by the emulator to report errors in
     * the course of TRACE_DEV_REG_QUERY_MALLOC event handling. In case of an
     * error, emulator sets this field to zero (invalid value for a process ID).
     */
    uint32_t        libc_pid;

    /* Process ID in context of which query is made. */
    uint32_t        query_pid;

    /* Code of the allocation routine, in context of which query has been made:
     *  1 - free
     *  2 - realloc
     */
    uint32_t        routine;

    /* Address in guest's virtual space of memory allocation descriptor for the
     * queried pointer. Descriptor, addressed by this field is initialized by
     * the emulator in response to the query.
     */
    target_ulong    desc;
} MallocDescQuery;
/* Helpers for addressing field in MallocDescQuery structure using which
 * emulator reports an error back to the guest.
 */
#define QUERY_RES_OFFSET        ((uint32_t)(ptrdiff_t)&(((MallocDescQuery*)0)->libc_pid))
#define QUERY_RES_ADDRESS(p)    (p + QUERY_RES_OFFSET)

/* Describes memory block that is being freed back to the heap. This structure
 * is passed along with TRACE_DEV_REG_FREE_PTR event. The entire structure is
 * initialized by the guest system before event is fired up. It is important to
 * remember that same structure (an exact copy) is also declared in the libc's
 * sources. So, every time a change is made to any of these two declaration,
 * another one must be also updated accordingly.
 */
typedef struct MallocFree {
    /* Pointer to be freed. */
    uint32_t    ptr;

    /* Id of the process that initialized libc instance, in which this free
     * is called. This field is used by the emulator to report errors in
     * the course of TRACE_DEV_REG_FREE_PTR event handling. In case of an
     * error, emulator sets this field to zero (invalid value for a process ID).
     */
    uint32_t    libc_pid;

    /* Process ID in context of which memory is being freed. */
    uint32_t    free_pid;
} MallocFree;
/* Helpers for addressing field in MallocFree structure, using which emulator
 * reports an error back to the guest.
 */
#define FREE_RES_OFFSET         ((uint32_t)(ptrdiff_t)&(((MallocFree*)0)->libc_pid))
#define FREE_RES_ADDRESS(p)     (p + FREE_RES_OFFSET)

/* Extends MallocDesc structure with additional information, used by memchecker.
 */
typedef struct MallocDescEx {
    /* Allocation descriptor this structure extends. */
    MallocDesc      malloc_desc;

    /* Call stack that lead to memory allocation. The array is arranged in
     * accending order, where entry at index 0 corresponds to the routine
     * that allocated memory. */
    target_ulong*   call_stack;

    /* Number of entries in call_stack array. */
    uint32_t        call_stack_count;

    /* Set of misc. flags. See MDESC_FLAG_XXX bellow. */
    uint32_t        flags;
} MallocDescEx;

/* Indicates that memory has been allocated before process started execution.
 * After a process has been forked, but before it actually starts executing,
 * allocations can be made in context of that process PID. This flag marks such
 * allocations in the process' allocation descriptors map.
 */
#define MDESC_FLAG_TRANSITION_ENTRY         0x00000001

/* Indicates that memory block has been inherited from the parent process.
 * When a process is forked from its parent process, the forked process inherits
 * a copy of the parent process' heap. Thus, all allocations that were recorded
 * for the parent process must be also recorded for the forked process. This
 * flag marks entries in the forked process' allocation descriptors map that
 * were copied over from the parent process' allocation descriptors map.
 */
#define MDESC_FLAG_INHERITED_ON_FORK        0x00000002

/* Describes a memory mapping of an execution module in the guest system. */
typedef struct MMRangeDesc {
    /* Starting address of mmapping of a module in the guest's address space. */
    target_ulong            map_start;

    /* Ending address of mmapping of a module in the guest's address space. */
    target_ulong            map_end;

    /* Mmapping's execution offset. */
    target_ulong            exec_offset;

    /* Image path of the module that has been mapped with this mmapping. */
    char*                   path;
} MMRangeDesc;

/* Enumerates returned values for insert routines implemeted for red-black
 * tree maps.
 */
typedef enum {
    /* New entry has been inserted into the map. */
    RBT_MAP_RESULT_ENTRY_INSERTED = 0,

    /* An entry, matching the new one already exists in the map. */
    RBT_MAP_RESULT_ENTRY_ALREADY_EXISTS,

    /* An existing entry, matching the new one has been replaced
    * with the new entry.
    */
    RBT_MAP_RESULT_ENTRY_REPLACED,

    /* An error has occurred when inserting entry into the map. */
    RBT_MAP_RESULT_ERROR = -1,
} RBTMapResult;

/* Encapsulates an array of guest addresses, sorted in accending order. */
typedef struct AddrArray {
    /* Array of addresses. */
    target_ulong*   addr;

    /* Number of elements in the array. */
    int             num;
} AddrArray;

// =============================================================================
// Inlines
// =============================================================================

/* Gets pointer returned to malloc caller for the given allocation decriptor.
 * Param:
 *  desc - Allocation descriptor.
 * Return:
 *  Pointer to the allocated memory returned to the malloc caller.
 */
static inline target_ulong
mallocdesc_get_user_ptr(const MallocDesc* desc)
{
    return desc->ptr + desc->prefix_size;
}

/* Gets total size of the allocated block for the given descriptor.
 * Param:
 *  desc - Descriptor for the memory block, allocated in malloc handler.
 * Return:
 *  Total size of memory block allocated in malloc handler.
 */
static inline uint32_t
mallocdesc_get_alloc_size(const MallocDesc* desc)
{
    return desc->prefix_size + desc->requested_bytes + desc->suffix_size;
}

/* Gets the end of the allocated block for the given descriptor.
 * Param:
 *  desc - Descriptor for the memory block, allocated in malloc handler.
 * Return:
 *  Pointer to the end of the allocated block (next byte past the block).
 */
static inline target_ulong
mallocdesc_get_alloc_end(const MallocDesc* desc)
{
    return desc->ptr + mallocdesc_get_alloc_size(desc);
}

/* Gets the end of the allocated block available to the user for the given
 * descriptor.
 * Param:
 *  desc - Descriptor for the memory block, allocated in malloc handler.
 * Return:
 *  Pointer to the end of the allocated block available to the user (next byte
 *  past the block - suffix guarding area).
 */
static inline target_ulong
mallocdesc_get_user_alloc_end(const MallocDesc* desc)
{
    return mallocdesc_get_user_ptr(desc) + desc->requested_bytes;
}

/* Checks if allocation has been made before process started execution.
 * Param:
 *  desc - Allocation descriptor to check.
 * Return:
 *  boolean: 1 if allocation has been made before process started execution,
 *  or 0 if allocation has been made after process started execution.
 */
static inline int
mallocdescex_is_transition_entry(const MallocDescEx* desc)
{
    return (desc->flags & MDESC_FLAG_TRANSITION_ENTRY) != 0;
}

/* Checks if allocation block has been inherited on fork.
 * Param:
 *  desc - Allocation descriptor to check.
 * Return:
 *  boolean: 1 if allocation has been inherited on fork, or 0 if allocation
 *  has been made by this process..
 */
static inline int
mallocdescex_is_inherited_on_fork(const MallocDescEx* desc)
{
    return (desc->flags & MDESC_FLAG_INHERITED_ON_FORK) != 0;
}

/* Gets offset for the given address inside a mapped module.
 * Param:
 *  address - Address to get offset for.
 * Return:
 *  Offset of the given address inside a mapped module, represented with the
 *  given mmaping range descriptor.
 */
static inline target_ulong
mmrangedesc_get_module_offset(const MMRangeDesc* rdesc, target_ulong address)
{
    return address - rdesc->map_start + rdesc->exec_offset;
}

/* Checks if given address is contained in the given address array.
 * Return:
 *  boolean: 1 if address is contained in the array, or zero if it's not.
 */
static inline int
addrarray_check(const AddrArray* addr_array, target_ulong addr)
{
    if (addr_array->num != 0) {
        int m_min = 0;
        int m_max = addr_array->num - 1;

        /* May be odd for THUMB mode. */
        addr &= ~1;
        /* Since array is sorted we can do binary search here. */
        while (m_min <= m_max) {
            const int m = (m_min + m_max) >> 1;
            const target_ulong saved = addr_array->addr[m];
            if (addr == saved) {
                return 1;
            }
            if (addr < saved) {
                m_max = m - 1;
            } else {
                m_min = m + 1;
            }
        }
    }
    return 0;
}

/* Adds an address to the address array.
 * Return:
 *  1  - Address has been added to the array.
 *  -1 - Address already exists in the array.
 *  0  - Unable to expand the array.
 */
static inline int
addrarray_add(AddrArray* addr_array, target_ulong addr)
{
    target_ulong* new_arr;
    int m_min;
    int m_max;

    /* May be odd for THUMB mode. */
    addr &= ~1;
    if (addr_array->num == 0) {
        /* First element. */
        addr_array->addr = qemu_malloc(sizeof(target_ulong));
        assert(addr_array->addr != NULL);
        if (addr_array->addr == NULL) {
            return 0;
        }
        *addr_array->addr = addr;
        addr_array->num++;
        return 1;
    }

    /* Using binary search find the place where to insert new address. */
    m_min = 0;
    m_max = addr_array->num - 1;
    while (m_min <= m_max) {
        const int m = (m_min + m_max) >> 1;
        const target_ulong saved = addr_array->addr[m];
        if (addr == saved) {
            return -1;
        }
        if (addr < saved) {
            m_max = m - 1;
        } else {
            m_min = m + 1;
        }
    }
    if (m_max < 0) {
        m_max = 0;
    }
    /* Expand the array. */
    new_arr = qemu_malloc(sizeof(target_ulong) * (addr_array->num + 1));
    assert(new_arr != NULL);
    if (new_arr == NULL) {
        return 0;
    }
    /* Copy preceding elements to the new array. */
    if (m_max != 0) {
        memcpy(new_arr, addr_array->addr, m_max * sizeof(target_ulong));
    }
    if (addr > addr_array->addr[m_max]) {
        new_arr[m_max] = addr_array->addr[m_max];
        m_max++;
    }
    /* Insert new address. */
    new_arr[m_max] = addr;
    /* Copy remaining elements to the new array. */
    if (m_max < addr_array->num) {
        memcpy(new_arr + m_max + 1, addr_array->addr + m_max,
               (addr_array->num - m_max) * sizeof(target_ulong));
    }
    /* Swap arrays. */
    qemu_free(addr_array->addr);
    addr_array->addr = new_arr;
    addr_array->num++;
    return 1;
}

#ifdef __cplusplus
};  /* end of extern "C" */
#endif

#endif  // QEMU_MEMCHECK_MEMCHECK_COMMON_H