Branch data Line data Source code
1 : : #include "mem.h"
2 : : #include "mem_internal.h"
3 : : #include <stdint.h>
4 : : #include <string.h>
5 : :
6 : : /**
7 : : * @brief Internal raw-buffer transfer core used by append and copy entry points
8 : : *
9 : : * This helper is the data-mode counterpart of @ref mem_core_string. It never
10 : : * scans for terminators and always interprets @p source_buffer_size_bytes as an
11 : : * exact byte count for raw payload transfer
12 : : *
13 : : * Transfer flags:
14 : : * - `TRANSFER_APPEND`: append exact source bytes after the current
15 : : * destination payload
16 : : * - `TRANSFER_REPLACE`: replace the current destination payload with the
17 : : * exact source byte range
18 : : *
19 : : * Self-aliasing is supported when @p source_buffer points inside the current
20 : : * destination allocation. The source range must stay inside the current
21 : : * logical destination payload, not merely inside allocator reserve. The helper
22 : : * stores the source offset before @ref m_resize and rebuilds the source
23 : : * pointer afterwards before copying with `memmove`
24 : : *
25 : : * Empty-source rules:
26 : : * - append with `NULL + 0` or an explicit zero-size source is a no-op
27 : : * - replace with `NULL + 0` or an explicit zero-size source clears the
28 : : * destination through @ref m_resize
29 : : *
30 : : * @param mode Binary mode flags. Raw-buffer cores only use transfer flags
31 : : * @param destination Pointer to the destination descriptor in data mode
32 : : * @param source_buffer_size_bytes Exact byte count to transfer
33 : : * @param source_buffer Pointer to exact source bytes
34 : : * @return `SUCCESS` on success; `FAILURE` otherwise
35 : : */
36 : 499 : Return mem_core_buffer(
37 : : const MEM_CORE_MODE mode,
38 : : memory *destination,
39 : : const size_t source_buffer_size_bytes,
40 : : const void *const source_buffer)
41 : : {
42 : : /* Status returned by this function through provide()
43 : : Default value assumes successful completion */
44 : 499 : Return status = SUCCESS;
45 : :
46 : : /* Transfer-mode subset extracted from the combined mode flags */
47 : 499 : const MEM_CORE_MODE transfer_mode = mode & TRANSFER_MASK;
48 : :
49 : : /* Number of destination elements represented by the exact source byte count */
50 : 499 : size_t transfer_elements = 0;
51 : :
52 : : /* Total destination element count after append or replace */
53 : 499 : size_t new_total_elements = 0;
54 : :
55 : : /* Byte offset where the transferred source bytes are written in destination */
56 : 499 : size_t offset_bytes = 0;
57 : :
58 : : /* Current logical destination payload size in bytes, used to validate self-aliased sources */
59 : 499 : size_t destination_bytes = 0;
60 : :
61 : : /* Offset of a self-aliased source from the beginning of destination data */
62 : 499 : size_t source_offset = 0;
63 : :
64 : : /* True when source_buffer points into the current destination allocation */
65 : 499 : bool source_is_inside_destination = false;
66 : :
67 [ - + ]: 499 : if(destination == NULL)
68 : : {
69 : 0 : report("Memory management; Destination must be non-NULL");
70 : 0 : provide(FAILURE);
71 : : }
72 : :
73 [ + + + + ]: 499 : if(destination->length > 0 && destination->data == NULL)
74 : : {
75 : 1 : report("Memory management; Descriptor has non-zero length with NULL data pointer");
76 : 1 : provide(FAILURE);
77 : : }
78 : :
79 [ + + ]: 498 : if(destination->is_string == true)
80 : : {
81 : 1 : report("Memory management; Destination must be in data mode, but it is a string");
82 : 1 : provide(FAILURE);
83 : : }
84 : :
85 [ - + ]: 497 : if(destination->single_element_size == 0)
86 : : {
87 : 0 : report("Memory management; Destination element size is zero (uninitialized)");
88 : 0 : provide(FAILURE);
89 : : }
90 : :
91 [ + + ]: 497 : if(source_buffer == NULL)
92 : : {
93 [ - + ]: 3 : if(source_buffer_size_bytes > 0)
94 : : {
95 : 0 : report("Memory management; Source is NULL while size is %zu",source_buffer_size_bytes);
96 : 0 : provide(FAILURE);
97 : : }
98 : :
99 [ - + ]: 3 : if(transfer_mode & TRANSFER_APPEND)
100 : : {
101 : 0 : provide(status);
102 : : }
103 : :
104 : 3 : run(m_resize(destination,0));
105 : 3 : provide(status);
106 : : }
107 : :
108 [ - + ]: 494 : if(source_buffer_size_bytes == 0)
109 : : {
110 [ # # ]: 0 : if(transfer_mode & TRANSFER_APPEND)
111 : : {
112 : 0 : provide(status);
113 : : }
114 : :
115 : 0 : run(m_resize(destination,0));
116 : 0 : provide(status);
117 : : }
118 : :
119 [ - + ]: 494 : if((source_buffer_size_bytes % destination->single_element_size) != 0)
120 : : {
121 : 0 : report("Memory management; Size %zu is not divisible by element size %zu",source_buffer_size_bytes,destination->single_element_size);
122 : 0 : provide(FAILURE);
123 : : }
124 : :
125 : 494 : transfer_elements = source_buffer_size_bytes / destination->single_element_size;
126 : :
127 [ + + ]: 494 : if(transfer_mode & TRANSFER_APPEND)
128 : : {
129 : 14 : run(mem_guarded_add(destination->length,transfer_elements,&new_total_elements));
130 : :
131 [ - + ]: 14 : if(CRITICAL & status)
132 : : {
133 : 0 : report("Memory management; Concatenation would overflow element count");
134 : 0 : provide(FAILURE);
135 : : }
136 : :
137 : 14 : run(mem_guarded_byte_size(destination,destination->length,&offset_bytes));
138 : :
139 [ - + ]: 14 : if(CRITICAL & status)
140 : : {
141 : 0 : report("Memory management; Destination byte offset overflows");
142 : 0 : provide(status);
143 : : }
144 : : } else {
145 : 480 : new_total_elements = transfer_elements;
146 : 480 : offset_bytes = 0;
147 : : }
148 : :
149 : 494 : run(mem_guarded_byte_size(destination,destination->length,&destination_bytes));
150 : :
151 [ + - + + : 494 : if((TRIUMPH & status) && destination->data != NULL && destination->actually_allocated_bytes > 0)
+ - ]
152 : : {
153 : : /* Use integer addresses so external source buffers can be compared without pointer-order UB */
154 : 409 : const uintptr_t destination_begin = (uintptr_t)destination->data;
155 : 409 : const uintptr_t source_begin = (uintptr_t)source_buffer;
156 : :
157 : : /* Both allocation and logical ends are derived from destination_begin and must not wrap */
158 [ + - ]: 409 : if(destination->actually_allocated_bytes > UINTPTR_MAX - destination_begin ||
159 [ - + ]: 409 : destination_bytes > UINTPTR_MAX - destination_begin)
160 : : {
161 : 0 : report("Memory management; Destination address range overflows");
162 : 0 : provide(FAILURE);
163 : : }
164 : :
165 : 409 : const uintptr_t allocation_end = destination_begin + destination->actually_allocated_bytes;
166 : 409 : const uintptr_t logical_end = destination_begin + destination_bytes;
167 : :
168 : : /* A source pointer inside the allocation is destination-owned, even if it points into spare capacity */
169 [ + + + + ]: 409 : if(source_begin >= destination_begin && source_begin < allocation_end)
170 : : {
171 : 7 : source_is_inside_destination = true;
172 : 7 : source_offset = source_begin - destination_begin;
173 : :
174 : : /* Destination-owned sources may only expose the current logical payload */
175 [ + - - + ]: 7 : if(source_begin > logical_end || source_buffer_size_bytes > destination_bytes - source_offset)
176 : : {
177 : 0 : report("Memory management; Source range exceeds destination logical bounds");
178 : 0 : provide(FAILURE);
179 : : }
180 : : }
181 : : }
182 : :
183 : : /* m_resize may call realloc, so any source_buffer pointer into destination data becomes invalid here */
184 : 494 : run(m_resize(destination,new_total_elements));
185 : :
186 [ + - ]: 494 : if(TRIUMPH & status)
187 : : {
188 : 494 : unsigned char *destination_data_rewritable = (unsigned char *)destination->data;
189 : :
190 [ - + ]: 494 : if(destination_data_rewritable == NULL)
191 : : {
192 : 0 : report("Memory management; Destination data pointer is NULL after m_resize");
193 : 0 : status = FAILURE;
194 : : } else {
195 : 494 : const unsigned char *source_data_view = (const unsigned char *)source_buffer;
196 : :
197 : : /* Rebase self-aliased sources onto the post-resize allocation using the saved offset */
198 [ + + ]: 494 : if(source_is_inside_destination == true)
199 : : {
200 : 7 : source_data_view = destination_data_rewritable + source_offset;
201 : : }
202 : :
203 : : /* memmove handles valid self-overlap between the rebuilt source slice and target location */
204 : 494 : memmove(destination_data_rewritable + offset_bytes,source_data_view,source_buffer_size_bytes);
205 : : }
206 : : }
207 : :
208 [ + - ]: 494 : if(TRIUMPH & status)
209 : : {
210 : 494 : destination->string_length = 0;
211 : 494 : destination->is_string = false;
212 : : }
213 : :
214 : 494 : provide(status);
215 : : }
|