alloc_buffer.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* Allocation from a fixed-size buffer.
  2. Copyright (C) 2017-2026 Free Software Foundation, Inc.
  3. This file is part of the GNU C Library.
  4. The GNU C Library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Lesser General Public
  6. License as published by the Free Software Foundation; either
  7. version 2.1 of the License, or (at your option) any later version.
  8. The GNU C Library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public
  13. License along with the GNU C Library; if not, see
  14. <https://www.gnu.org/licenses/>. */
  15. /* Allocation buffers are used to carve out sub-allocations from a
  16. larger allocation. Their primary application is in writing NSS
  17. modules, which receive a caller-allocated buffer in which they are
  18. expected to store variable-length results:
  19. void *buffer = ...;
  20. size_t buffer_size = ...;
  21. struct alloc_buffer buf = alloc_buffer_create (buffer, buffer_size);
  22. result->gr_name = alloc_buffer_copy_string (&buf, name);
  23. // Allocate a list of group_count groups and copy strings into it.
  24. char **group_list = alloc_buffer_alloc_array
  25. (&buf, char *, group_count + 1);
  26. if (group_list == NULL)
  27. return ...; // Request a larger buffer.
  28. for (int i = 0; i < group_count; ++i)
  29. group_list[i] = alloc_buffer_copy_string (&buf, group_list_src[i]);
  30. group_list[group_count] = NULL;
  31. ...
  32. if (alloc_buffer_has_failed (&buf))
  33. return ...; // Request a larger buffer.
  34. result->gr_mem = group_list;
  35. ...
  36. Note that it is not necessary to check the results of individual
  37. allocation operations if the returned pointer is not dereferenced.
  38. Allocation failure is sticky, so one check using
  39. alloc_buffer_has_failed at the end covers all previous failures.
  40. A different use case involves combining multiple heap allocations
  41. into a single, large one. In the following example, an array of
  42. doubles and an array of ints is allocated:
  43. size_t double_array_size = ...;
  44. size_t int_array_size = ...;
  45. void *heap_ptr;
  46. struct alloc_buffer buf = alloc_buffer_allocate
  47. (double_array_size * sizeof (double) + int_array_size * sizeof (int),
  48. &heap_ptr);
  49. _Static_assert (__alignof__ (double) >= __alignof__ (int),
  50. "no padding after double array");
  51. double *double_array = alloc_buffer_alloc_array
  52. (&buf, double, double_array_size);
  53. int *int_array = alloc_buffer_alloc_array (&buf, int, int_array_size);
  54. if (alloc_buffer_has_failed (&buf))
  55. return ...; // Report error.
  56. ...
  57. free (heap_ptr);
  58. The advantage over manual coding is that the computation of the
  59. allocation size does not need an overflow check. In case of an
  60. overflow, one of the subsequent allocations from the buffer will
  61. fail. The initial size computation is checked for consistency at
  62. run time, too. */
  63. #ifndef _ALLOC_BUFFER_H
  64. #define _ALLOC_BUFFER_H
  65. #include <inttypes.h>
  66. #include <stdbool.h>
  67. #include <stddef.h>
  68. #include <stdlib.h>
  69. #include <sys/param.h>
  70. /* struct alloc_buffer objects refer to a region of bytes in memory of a
  71. fixed size. The functions below can be used to allocate single
  72. objects and arrays from this memory region, or write to its end.
  73. On allocation failure (or if an attempt to write beyond the end of
  74. the buffer with one of the copy functions), the buffer enters a
  75. failed state.
  76. struct alloc_buffer objects can be copied. The backing buffer will
  77. be shared, but the current write position will be independent.
  78. Conceptually, the memory region consists of a current write pointer
  79. and a limit, beyond which the write pointer cannot move. */
  80. struct alloc_buffer
  81. {
  82. /* uintptr_t is used here to simplify the alignment code, and to
  83. avoid issues undefined subtractions if the buffer covers more
  84. than half of the address space (which would result in differences
  85. which could not be represented as a ptrdiff_t value). */
  86. uintptr_t __alloc_buffer_current;
  87. uintptr_t __alloc_buffer_end;
  88. };
  89. enum
  90. {
  91. /* The value for the __alloc_buffer_current member which marks the
  92. buffer as invalid (together with a zero-length buffer). */
  93. __ALLOC_BUFFER_INVALID_POINTER = 0,
  94. };
  95. /* Internal function. Terminate the process using __libc_fatal. */
  96. void __libc_alloc_buffer_create_failure (void *start, size_t size);
  97. #ifndef _ISOMAC
  98. libc_hidden_proto (__libc_alloc_buffer_create_failure)
  99. #endif
  100. /* Create a new allocation buffer. The byte range from START to START
  101. + SIZE - 1 must be valid, and the allocation buffer allocates
  102. objects from that range. If START is NULL (so that SIZE must be
  103. 0), the buffer is marked as failed immediately. */
  104. static inline struct alloc_buffer
  105. alloc_buffer_create (void *start, size_t size)
  106. {
  107. uintptr_t current = (uintptr_t) start;
  108. uintptr_t end = (uintptr_t) start + size;
  109. if (end < current)
  110. __libc_alloc_buffer_create_failure (start, size);
  111. return (struct alloc_buffer) { current, end };
  112. }
  113. /* Internal function. See alloc_buffer_allocate below. */
  114. struct alloc_buffer __libc_alloc_buffer_allocate (size_t size, void **pptr)
  115. __attribute__ ((nonnull (2)));
  116. #ifndef _ISOMAC
  117. libc_hidden_proto (__libc_alloc_buffer_allocate)
  118. #endif
  119. /* Allocate a buffer of SIZE bytes using malloc. The returned buffer
  120. is in a failed state if malloc fails. *PPTR points to the start of
  121. the buffer and can be used to free it later, after the returned
  122. buffer has been freed. */
  123. static __always_inline __attribute__ ((nonnull (2)))
  124. struct alloc_buffer alloc_buffer_allocate (size_t size, void **pptr)
  125. {
  126. return __libc_alloc_buffer_allocate (size, pptr);
  127. }
  128. /* Mark the buffer as failed. */
  129. static inline void __attribute__ ((nonnull (1)))
  130. alloc_buffer_mark_failed (struct alloc_buffer *buf)
  131. {
  132. buf->__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER;
  133. buf->__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER;
  134. }
  135. /* Return the remaining number of bytes in the buffer. */
  136. static __always_inline __attribute__ ((nonnull (1))) size_t
  137. alloc_buffer_size (const struct alloc_buffer *buf)
  138. {
  139. return buf->__alloc_buffer_end - buf->__alloc_buffer_current;
  140. }
  141. /* Return true if the buffer has been marked as failed. */
  142. static inline bool __attribute__ ((nonnull (1)))
  143. alloc_buffer_has_failed (const struct alloc_buffer *buf)
  144. {
  145. return buf->__alloc_buffer_current == __ALLOC_BUFFER_INVALID_POINTER;
  146. }
  147. /* Add a single byte to the buffer (consuming the space for this
  148. byte). Mark the buffer as failed if there is not enough room. */
  149. static inline void __attribute__ ((nonnull (1)))
  150. alloc_buffer_add_byte (struct alloc_buffer *buf, unsigned char b)
  151. {
  152. if (__glibc_likely (buf->__alloc_buffer_current < buf->__alloc_buffer_end))
  153. {
  154. *(unsigned char *) buf->__alloc_buffer_current = b;
  155. ++buf->__alloc_buffer_current;
  156. }
  157. else
  158. alloc_buffer_mark_failed (buf);
  159. }
  160. /* Obtain a pointer to LENGTH bytes in BUF, and consume these bytes.
  161. NULL is returned if there is not enough room, and the buffer is
  162. marked as failed, or if the buffer has already failed.
  163. (Zero-length allocations from an empty buffer which has not yet
  164. failed succeed.) The buffer contents is not modified. */
  165. static inline __attribute__ ((nonnull (1))) void *
  166. alloc_buffer_alloc_bytes (struct alloc_buffer *buf, size_t length)
  167. {
  168. if (length <= alloc_buffer_size (buf))
  169. {
  170. void *result = (void *) buf->__alloc_buffer_current;
  171. buf->__alloc_buffer_current += length;
  172. return result;
  173. }
  174. else
  175. {
  176. alloc_buffer_mark_failed (buf);
  177. return NULL;
  178. }
  179. }
  180. /* Internal function. Statically assert that the type size is
  181. constant and valid. */
  182. static __always_inline size_t
  183. __alloc_buffer_assert_size (size_t size)
  184. {
  185. if (!__builtin_constant_p (size))
  186. {
  187. __errordecl (error, "type size is not constant");
  188. error ();
  189. }
  190. else if (size == 0)
  191. {
  192. __errordecl (error, "type size is zero");
  193. error ();
  194. }
  195. return size;
  196. }
  197. /* Internal function. Statically assert that the type alignment is
  198. constant and valid. */
  199. static __always_inline size_t
  200. __alloc_buffer_assert_align (size_t align)
  201. {
  202. if (!__builtin_constant_p (align))
  203. {
  204. __errordecl (error, "type alignment is not constant");
  205. error ();
  206. }
  207. else if (align == 0)
  208. {
  209. __errordecl (error, "type alignment is zero");
  210. error ();
  211. }
  212. else if (!powerof2 (align))
  213. {
  214. __errordecl (error, "type alignment is not a power of two");
  215. error ();
  216. }
  217. return align;
  218. }
  219. /* Internal function. Obtain a pointer to an object. */
  220. static inline __attribute__ ((nonnull (1))) void *
  221. __alloc_buffer_alloc (struct alloc_buffer *buf, size_t size, size_t align)
  222. {
  223. if (size == 1 && align == 1)
  224. return alloc_buffer_alloc_bytes (buf, size);
  225. uintptr_t current = buf->__alloc_buffer_current;
  226. uintptr_t aligned = roundup (current, align);
  227. uintptr_t new_current = aligned + size;
  228. if (aligned >= current /* No overflow in align step. */
  229. && new_current >= size /* No overflow in size computation. */
  230. && new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
  231. {
  232. buf->__alloc_buffer_current = new_current;
  233. return (void *) aligned;
  234. }
  235. else
  236. {
  237. alloc_buffer_mark_failed (buf);
  238. return NULL;
  239. }
  240. }
  241. /* Obtain a TYPE * pointer to an object in BUF of TYPE. Consume these
  242. bytes from the buffer. Return NULL and mark the buffer as failed
  243. if there is not enough room in the buffer, or if the buffer has
  244. failed before. */
  245. #define alloc_buffer_alloc(buf, type) \
  246. ((type *) __alloc_buffer_alloc \
  247. (buf, __alloc_buffer_assert_size (sizeof (type)), \
  248. __alloc_buffer_assert_align (__alignof__ (type))))
  249. /* Internal function. Obtain a pointer to an object which is
  250. subsequently added. */
  251. static inline const __attribute__ ((nonnull (1))) void *
  252. __alloc_buffer_next (struct alloc_buffer *buf, size_t align)
  253. {
  254. if (align == 1)
  255. return (const void *) buf->__alloc_buffer_current;
  256. uintptr_t current = buf->__alloc_buffer_current;
  257. uintptr_t aligned = roundup (current, align);
  258. if (aligned >= current /* No overflow in align step. */
  259. && aligned <= buf->__alloc_buffer_end) /* Room in buffer. */
  260. {
  261. buf->__alloc_buffer_current = aligned;
  262. return (const void *) aligned;
  263. }
  264. else
  265. {
  266. alloc_buffer_mark_failed (buf);
  267. return NULL;
  268. }
  269. }
  270. /* Like alloc_buffer_alloc, but do not advance the pointer beyond the
  271. object (so a subsequent call to alloc_buffer_next or
  272. alloc_buffer_alloc returns the same pointer). Note that the buffer
  273. is still aligned according to the requirements of TYPE, potentially
  274. consuming buffer space. The effect of this function is similar to
  275. allocating a zero-length array from the buffer.
  276. It is possible to use the return pointer to write to the buffer and
  277. consume the written bytes using alloc_buffer_alloc_bytes (which
  278. does not change the buffer contents), but the calling code needs to
  279. perform manual length checks using alloc_buffer_size. For example,
  280. to read as many int32_t values that are available in the input file
  281. and can fit into the remaining buffer space, you can use this:
  282. int32_t array = alloc_buffer_next (buf, int32_t);
  283. size_t ret = fread (array, sizeof (int32_t),
  284. alloc_buffer_size (buf) / sizeof (int32_t), fp);
  285. if (ferror (fp))
  286. handle_error ();
  287. alloc_buffer_alloc_array (buf, int32_t, ret);
  288. The alloc_buffer_alloc_array call makes the actually-used part of
  289. the buffer permanent. The remaining part of the buffer (not filled
  290. with data from the file) can be used for something else.
  291. This manual length checking can easily introduce errors, so this
  292. coding style is not recommended. */
  293. #define alloc_buffer_next(buf, type) \
  294. ((type *) __alloc_buffer_next \
  295. (buf, __alloc_buffer_assert_align (__alignof__ (type))))
  296. /* Internal function. Allocate an array. */
  297. void * __libc_alloc_buffer_alloc_array (struct alloc_buffer *buf,
  298. size_t size, size_t align,
  299. size_t count)
  300. __attribute__ ((nonnull (1)));
  301. #ifndef _ISOMAC
  302. libc_hidden_proto (__libc_alloc_buffer_alloc_array)
  303. #endif
  304. /* Obtain a TYPE * pointer to an array of COUNT objects in BUF of
  305. TYPE. Consume these bytes from the buffer. Return NULL and mark
  306. the buffer as failed if there is not enough room in the buffer,
  307. or if the buffer has failed before. (Zero-length allocations from
  308. an empty buffer which has not yet failed succeed.) */
  309. #define alloc_buffer_alloc_array(buf, type, count) \
  310. ((type *) __libc_alloc_buffer_alloc_array \
  311. (buf, __alloc_buffer_assert_size (sizeof (type)), \
  312. __alloc_buffer_assert_align (__alignof__ (type)), \
  313. count))
  314. /* Internal function. See alloc_buffer_copy_bytes below. */
  315. struct alloc_buffer __libc_alloc_buffer_copy_bytes (struct alloc_buffer,
  316. const void *, size_t)
  317. __attribute__ ((nonnull (2)));
  318. #ifndef _ISOMAC
  319. libc_hidden_proto (__libc_alloc_buffer_copy_bytes)
  320. #endif
  321. /* Copy SIZE bytes starting at SRC into the buffer. If there is not
  322. enough room in the buffer, the buffer is marked as failed. No
  323. alignment of the buffer is performed. */
  324. static inline __attribute__ ((nonnull (1, 2))) void
  325. alloc_buffer_copy_bytes (struct alloc_buffer *buf, const void *src, size_t size)
  326. {
  327. *buf = __libc_alloc_buffer_copy_bytes (*buf, src, size);
  328. }
  329. /* Internal function. See alloc_buffer_copy_string below. */
  330. struct alloc_buffer __libc_alloc_buffer_copy_string (struct alloc_buffer,
  331. const char *)
  332. __attribute__ ((nonnull (2)));
  333. #ifndef _ISOMAC
  334. libc_hidden_proto (__libc_alloc_buffer_copy_string)
  335. #endif
  336. /* Copy the string at SRC into the buffer, including its null
  337. terminator. If there is not enough room in the buffer, the buffer
  338. is marked as failed. Return a pointer to the string. */
  339. static inline __attribute__ ((nonnull (1, 2))) char *
  340. alloc_buffer_copy_string (struct alloc_buffer *buf, const char *src)
  341. {
  342. char *result = (char *) buf->__alloc_buffer_current;
  343. *buf = __libc_alloc_buffer_copy_string (*buf, src);
  344. if (alloc_buffer_has_failed (buf))
  345. result = NULL;
  346. return result;
  347. }
  348. #endif /* _ALLOC_BUFFER_H */