10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
- #include "heap.h"
13
+ #include <string.h>
14
14
15
- #if defined(__aarch64__ ) || defined(__ARM64__ ) || defined(_M_ARM64 )
16
- #define DEBUG_BREAK () asm("brk #0x0; nop")
17
- #elif defined(_M_X64 ) || defined(__amd64__ ) || defined(__x86_64__ ) || defined(_M_AMD64 )
18
- #define DEBUG_BREAK () asm("int3; nop")
19
- #else
20
- #error ("only aarch64 and x86_64 are supported")
21
- #endif
15
+ #include "heap.h"
22
16
23
- /* We allocate a buffer in the remote process that it populates with metadata
24
- * describing each heap entry it enumerates. We then read the contents of the
25
- * buffer, and individual heap entry contents, with process_vm_readv.
26
- *
27
- * The buffer is interpreted as an array of 8-byte pairs. The first pair
28
- * contains metadata describing the buffer itself: max valid index (e.g. size of
29
- * the buffer) and next index (e.g. write cursor/position). Each subsequent pair
30
- * describes the address and length of a heap entry in the remote process.
17
+ /* The heap metadata buffer is interpreted as an array of 8-byte pairs. The
18
+ * first pair contains metadata describing the buffer itself: max valid index
19
+ * (e.g. size of the buffer) and next index (e.g. write cursor/position). Each
20
+ * subsequent pair describes the address and length of a heap entry in the
21
+ * remote process. A 4KiB page provides sufficient space for the header and
22
+ * 255 (address, length) pairs.
31
23
*
32
24
* ------------
33
25
* | uint64_t | max valid index (e.g. sizeof(buffer) / sizeof(uint64_t))
52
44
* ------------
53
45
*/
54
46
55
- // NOTE: this function cannot call any other functions and must only use
56
- // relative branches. We could inline assembly instead, but C is more reabable
57
- // and maintainable.
58
- static void remote_callback_start (unsigned long base , unsigned long size , void * arg ) {
47
+ #define MAX_VALID_IDX 0
48
+ #define NEXT_FREE_IDX 1
49
+ #define HEADER_SIZE 2
50
+ #define ENTRY_SIZE 2
51
+
52
+ #if defined(__aarch64__ ) || defined(__ARM64__ ) || defined(_M_ARM64 )
53
+ #define DEBUG_BREAK () asm("brk #0x0")
54
+ #elif defined(_M_X64 ) || defined(__amd64__ ) || defined(__x86_64__ ) || defined(_M_AMD64 )
55
+ #define DEBUG_BREAK () asm("int3; nop")
56
+ #else
57
+ #error ("only aarch64 and x86_64 are supported")
58
+ #endif
59
+
60
+ // Callback for heap_iterate. Because this function is meant to be copied to
61
+ // a different process for execution, it must not make any function calls. It
62
+ // could be written as asm, but simple C is more readable/maintainable and
63
+ // should consistently compile to movable, position-independent code.
64
+ static void heap_iterate_callback (unsigned long base , unsigned long size , void * arg ) {
59
65
volatile uint64_t * data = (uint64_t * )arg ;
60
- while (data [HEAP_ITERATE_DATA_NEXT_FREE_IDX ] >= data [HEAP_ITERATE_DATA_MAX_VALID_IDX ]) {
66
+ while (data [NEXT_FREE_IDX ] >= data [MAX_VALID_IDX ]) {
67
+ // SIGTRAP indicates the buffer is full and needs to be drained before more
68
+ // entries can be written.
61
69
DEBUG_BREAK ();
62
70
}
63
- data [data [HEAP_ITERATE_DATA_NEXT_FREE_IDX ]++ ] = base ;
64
- data [data [HEAP_ITERATE_DATA_NEXT_FREE_IDX ]++ ] = size ;
71
+ data [data [NEXT_FREE_IDX ]++ ] = base ;
72
+ data [data [NEXT_FREE_IDX ]++ ] = size ;
65
73
}
66
74
67
- // NOTE: this function is here to mark the end of remote_callback_start and is never
68
- // called.
69
- static void remote_callback_end () {}
75
+ // Placeholer function to mark the end of the remote callback code.
76
+ static void heap_iterate_callback_end () {}
77
+
78
+ void * heap_iterate_callback_start () {
79
+ return (void * )heap_iterate_callback ;
80
+ }
70
81
71
- void * heap_callback_start () {
72
- return (void * ) remote_callback_start ;
82
+ size_t heap_iterate_callback_len () {
83
+ return (size_t )( heap_iterate_callback_end - heap_iterate_callback ) ;
73
84
}
74
85
75
- size_t heap_callback_len () {
76
- return (size_t )(remote_callback_end - remote_callback_start );
86
+ bool heap_iterate_metadata_init (void * data , size_t len ) {
87
+ uint64_t * metadata = data ;
88
+ const uint64_t max_entries = len / sizeof (uint64_t );
89
+ if (max_entries < HEADER_SIZE + ENTRY_SIZE )
90
+ return false;
91
+
92
+ memset (data , 0 , len );
93
+ metadata [MAX_VALID_IDX ] = max_entries ;
94
+ metadata [NEXT_FREE_IDX ] = HEADER_SIZE ;
95
+ return true;
77
96
}
97
+
98
+ bool heap_iterate_metadata_process (
99
+ void * data , size_t len , void * callback_context , heap_iterate_entry_callback_t callback ) {
100
+ uint64_t * metadata = data ;
101
+ const uint64_t max_entries = len / sizeof (uint64_t );
102
+
103
+ if (metadata [MAX_VALID_IDX ] != max_entries ||
104
+ metadata [NEXT_FREE_IDX ] > max_entries )
105
+ return false;
106
+
107
+ for (size_t i = HEADER_SIZE ; i < metadata [NEXT_FREE_IDX ]; i += ENTRY_SIZE ) {
108
+ const uint64_t base = metadata [i ];
109
+ const uint64_t size = metadata [i + 1 ];
110
+ callback (callback_context , base , size );
111
+ }
112
+
113
+ return true;
114
+ }
0 commit comments