s2n_mem.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License").
  5. * You may not use this file except in compliance with the License.
  6. * A copy of the License is located at
  7. *
  8. * http://aws.amazon.com/apache2.0
  9. *
  10. * or in the "license" file accompanying this file. This file is distributed
  11. * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
  12. * express or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. #define _DEFAULT_SOURCE 1
  16. #if defined(S2N_FEATURES_AVAILABLE)
  17. #include <features.h>
  18. #endif
  19. #include <limits.h>
  20. #include <stdint.h>
  21. #include <stdlib.h>
  22. #include <sys/mman.h>
  23. #include <sys/param.h>
  24. #include <unistd.h>
  25. #include "error/s2n_errno.h"
  26. #include "utils/s2n_blob.h"
  27. #include "utils/s2n_mem.h"
  28. #include "utils/s2n_safety.h"
  29. static uint32_t page_size = 4096;
  30. static bool initialized = false;
  31. static int s2n_mem_init_impl(void);
  32. static int s2n_mem_cleanup_impl(void);
  33. static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size);
  34. static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size);
  35. static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
  36. static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
  37. static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl;
  38. static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl;
  39. static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl;
  40. static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl;
  41. static int s2n_mem_init_impl(void)
  42. {
  43. long sysconf_rc = sysconf(_SC_PAGESIZE);
  44. /* sysconf must not error, and page_size cannot be 0 */
  45. POSIX_ENSURE_GT(sysconf_rc, 0);
  46. /* page_size must be a valid uint32 */
  47. long max_page_size = MIN(UINT32_MAX, LONG_MAX);
  48. POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
  49. page_size = (uint32_t) sysconf_rc;
  50. if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
  51. s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
  52. s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
  53. }
  54. return S2N_SUCCESS;
  55. }
  56. static int s2n_mem_cleanup_impl(void)
  57. {
  58. page_size = 4096;
  59. s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
  60. s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
  61. return S2N_SUCCESS;
  62. }
  63. static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size)
  64. {
  65. /* Perform a best-effort `munlock`: ignore any errors during unlocking. */
  66. munlock(ptr, size);
  67. free(ptr);
  68. return S2N_SUCCESS;
  69. }
  70. static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size)
  71. {
  72. free(ptr);
  73. return S2N_SUCCESS;
  74. }
  75. static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
  76. {
  77. POSIX_ENSURE_REF(ptr);
  78. /* Page aligned allocation required for mlock */
  79. uint32_t allocate;
  80. POSIX_GUARD(s2n_align_to(requested, page_size, &allocate));
  81. *ptr = NULL;
  82. POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC);
  83. *allocated = allocate;
  84. /*
  85. ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because
  86. ** both need to be able to dump pages to function. It's how they map heap output.
  87. */
  88. #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING)
  89. if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) {
  90. POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
  91. POSIX_BAIL(S2N_ERR_MADVISE);
  92. }
  93. #endif
  94. if (mlock(*ptr, *allocated) != 0) {
  95. /* When mlock fails, no memory will be locked, so we don't use munlock on free */
  96. POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
  97. POSIX_BAIL(S2N_ERR_MLOCK);
  98. }
  99. POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
  100. return S2N_SUCCESS;
  101. }
  102. static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
  103. {
  104. *ptr = malloc(requested);
  105. POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
  106. *allocated = requested;
  107. return S2N_SUCCESS;
  108. }
  109. int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
  110. s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
  111. {
  112. POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED);
  113. POSIX_ENSURE_REF(mem_init_callback);
  114. POSIX_ENSURE_REF(mem_cleanup_callback);
  115. POSIX_ENSURE_REF(mem_malloc_callback);
  116. POSIX_ENSURE_REF(mem_free_callback);
  117. s2n_mem_init_cb = mem_init_callback;
  118. s2n_mem_cleanup_cb = mem_cleanup_callback;
  119. s2n_mem_malloc_cb = mem_malloc_callback;
  120. s2n_mem_free_cb = mem_free_callback;
  121. return S2N_SUCCESS;
  122. }
  123. int s2n_alloc(struct s2n_blob *b, uint32_t size)
  124. {
  125. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  126. POSIX_ENSURE_REF(b);
  127. const struct s2n_blob temp = { 0 };
  128. *b = temp;
  129. POSIX_GUARD(s2n_realloc(b, size));
  130. return S2N_SUCCESS;
  131. }
  132. /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
  133. bool s2n_blob_is_growable(const struct s2n_blob *b)
  134. {
  135. return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
  136. }
  137. /* Tries to realloc the requested bytes.
  138. * If successful, updates *b.
  139. * If failed, *b remains unchanged
  140. */
  141. int s2n_realloc(struct s2n_blob *b, uint32_t size)
  142. {
  143. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  144. POSIX_ENSURE_REF(b);
  145. POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
  146. if (size == 0) {
  147. return s2n_free(b);
  148. }
  149. /* blob already has space for the request */
  150. if (size <= b->allocated) {
  151. if (size < b->size) {
  152. /* Zero the existing blob memory before the we release it */
  153. struct s2n_blob slice = { 0 };
  154. POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
  155. POSIX_GUARD(s2n_blob_zero(&slice));
  156. }
  157. b->size = size;
  158. return S2N_SUCCESS;
  159. }
  160. struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
  161. if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
  162. S2N_ERROR_PRESERVE_ERRNO();
  163. }
  164. POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
  165. POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
  166. if (b->size) {
  167. POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
  168. }
  169. if (b->allocated) {
  170. POSIX_GUARD(s2n_free(b));
  171. }
  172. *b = new_memory;
  173. return S2N_SUCCESS;
  174. }
  175. int s2n_free_object(uint8_t **p_data, uint32_t size)
  176. {
  177. POSIX_ENSURE_REF(p_data);
  178. if (*p_data == NULL) {
  179. return S2N_SUCCESS;
  180. }
  181. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  182. struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
  183. /* s2n_free() will call free() even if it returns error (for a growable blob).
  184. ** This makes sure *p_data is not used after free() */
  185. *p_data = NULL;
  186. return s2n_free(&b);
  187. }
  188. int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
  189. {
  190. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  191. POSIX_ENSURE_REF(to);
  192. POSIX_ENSURE_REF(from);
  193. POSIX_ENSURE_EQ(to->size, 0);
  194. POSIX_ENSURE_EQ(to->data, NULL);
  195. POSIX_ENSURE_NE(from->size, 0);
  196. POSIX_ENSURE_NE(from->data, NULL);
  197. POSIX_GUARD(s2n_alloc(to, from->size));
  198. POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
  199. return S2N_SUCCESS;
  200. }
  201. int s2n_mem_init(void)
  202. {
  203. POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
  204. initialized = true;
  205. return S2N_SUCCESS;
  206. }
  207. bool s2n_mem_is_init(void)
  208. {
  209. return initialized;
  210. }
  211. uint32_t s2n_mem_get_page_size(void)
  212. {
  213. return page_size;
  214. }
  215. int s2n_mem_cleanup(void)
  216. {
  217. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  218. POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
  219. initialized = false;
  220. return S2N_SUCCESS;
  221. }
  222. int s2n_free(struct s2n_blob *b)
  223. {
  224. /* To avoid memory leaks, don't exit the function until the memory
  225. has been freed */
  226. int zero_rc = s2n_blob_zero(b);
  227. POSIX_GUARD(s2n_free_without_wipe(b));
  228. return zero_rc;
  229. }
  230. int s2n_free_without_wipe(struct s2n_blob *b)
  231. {
  232. POSIX_PRECONDITION(s2n_blob_validate(b));
  233. POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
  234. POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
  235. if (b->data) {
  236. POSIX_ENSURE(s2n_mem_free_cb(b->data, b->allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
  237. }
  238. *b = (struct s2n_blob){ 0 };
  239. return S2N_SUCCESS;
  240. }
  241. int s2n_free_or_wipe(struct s2n_blob *b)
  242. {
  243. POSIX_ENSURE_REF(b);
  244. int zero_rc = s2n_blob_zero(b);
  245. if (b->allocated) {
  246. POSIX_GUARD(s2n_free_without_wipe(b));
  247. }
  248. return zero_rc;
  249. }