LCOV - code coverage report
Current view: top level - utils - s2n_mem.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 152 185 82.2 %
Date: 2025-08-15 07:28:39 Functions: 17 21 81.0 %
Branches: 92 230 40.0 %

           Branch data     Line data    Source code
       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                 :            : 
      16                 :            : #define _DEFAULT_SOURCE 1
      17                 :            : #if defined(S2N_FEATURES_AVAILABLE)
      18                 :            :     #include <features.h>
      19                 :            : #endif
      20                 :            : 
      21                 :            : #include <limits.h>
      22                 :            : #include <stdint.h>
      23                 :            : #include <stdlib.h>
      24                 :            : #include <sys/mman.h>
      25                 :            : #include <sys/param.h>
      26                 :            : #include <unistd.h>
      27                 :            : 
      28                 :            : #include "error/s2n_errno.h"
      29                 :            : #include "utils/s2n_blob.h"
      30                 :            : #include "utils/s2n_mem.h"
      31                 :            : #include "utils/s2n_safety.h"
      32                 :            : 
      33                 :            : static uint32_t page_size = 4096;
      34                 :            : static bool initialized = false;
      35                 :            : 
      36                 :            : static int s2n_mem_init_impl(void);
      37                 :            : static int s2n_mem_cleanup_impl(void);
      38                 :            : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size);
      39                 :            : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size);
      40                 :            : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
      41                 :            : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated);
      42                 :            : 
      43                 :            : static s2n_mem_init_callback s2n_mem_init_cb = s2n_mem_init_impl;
      44                 :            : static s2n_mem_cleanup_callback s2n_mem_cleanup_cb = s2n_mem_cleanup_impl;
      45                 :            : static s2n_mem_malloc_callback s2n_mem_malloc_cb = s2n_mem_malloc_mlock_impl;
      46                 :            : static s2n_mem_free_callback s2n_mem_free_cb = s2n_mem_free_mlock_impl;
      47                 :            : 
      48                 :            : static int s2n_mem_init_impl(void)
      49                 :        542 : {
      50                 :        542 :     long sysconf_rc = sysconf(_SC_PAGESIZE);
      51                 :            : 
      52                 :            :     /* sysconf must not error, and page_size cannot be 0 */
      53 [ -  + ][ #  # ]:        542 :     POSIX_ENSURE_GT(sysconf_rc, 0);
      54                 :            : 
      55                 :            :     /* page_size must be a valid uint32 */
      56                 :        542 :     long max_page_size = MIN(UINT32_MAX, LONG_MAX);
      57 [ -  + ][ #  # ]:        542 :     POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
      58                 :        542 :     page_size = (uint32_t) sysconf_rc;
      59                 :            : 
      60 [ +  - ][ #  # ]:        542 :     if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
      61                 :        542 :         s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
      62                 :        542 :         s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
      63                 :        542 :     }
      64                 :        542 :     return S2N_SUCCESS;
      65                 :        542 : }
      66                 :            : 
      67                 :            : static int s2n_mem_cleanup_impl(void)
      68                 :        542 : {
      69                 :        542 :     page_size = 4096;
      70                 :        542 :     s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
      71                 :        542 :     s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
      72                 :        542 :     return S2N_SUCCESS;
      73                 :        542 : }
      74                 :            : 
      75                 :            : static int s2n_mem_free_mlock_impl(void *ptr, uint32_t size)
      76                 :          0 : {
      77                 :            :     /* Perform a best-effort `munlock`: ignore any errors during unlocking. */
      78                 :          0 :     munlock(ptr, size);
      79                 :          0 :     free(ptr);
      80                 :          0 :     return S2N_SUCCESS;
      81                 :          0 : }
      82                 :            : 
      83                 :            : static int s2n_mem_free_no_mlock_impl(void *ptr, uint32_t size)
      84                 :    7702431 : {
      85                 :    7702431 :     free(ptr);
      86                 :            : 
      87                 :    7702431 :     return S2N_SUCCESS;
      88                 :    7702431 : }
      89                 :            : 
      90                 :            : static int s2n_mem_malloc_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
      91                 :          0 : {
      92 [ #  # ][ #  # ]:          0 :     POSIX_ENSURE_REF(ptr);
      93                 :            : 
      94                 :            :     /* Page aligned allocation required for mlock */
      95                 :          0 :     uint32_t allocate = 0;
      96                 :            : 
      97         [ #  # ]:          0 :     POSIX_GUARD(s2n_align_to(requested, page_size, &allocate));
      98                 :            : 
      99                 :          0 :     *ptr = NULL;
     100 [ #  # ][ #  # ]:          0 :     POSIX_ENSURE(posix_memalign(ptr, page_size, allocate) == 0, S2N_ERR_ALLOC);
     101                 :          0 :     *allocated = allocate;
     102                 :            : 
     103                 :            : /*
     104                 :            : ** We disable MAD_DONTDUMP when fuzz-testing or using the address sanitizer because
     105                 :            : ** both need to be able to dump pages to function. It's how they map heap output.
     106                 :            : */
     107                 :          0 : #if defined(MADV_DONTDUMP) && !defined(S2N_ADDRESS_SANITIZER) && !defined(S2N_FUZZ_TESTING)
     108         [ #  # ]:          0 :     if (madvise(*ptr, *allocated, MADV_DONTDUMP) != 0) {
     109         [ #  # ]:          0 :         POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
     110         [ #  # ]:          0 :         POSIX_BAIL(S2N_ERR_MADVISE);
     111                 :          0 :     }
     112                 :          0 : #endif
     113                 :            : 
     114         [ #  # ]:          0 :     if (mlock(*ptr, *allocated) != 0) {
     115                 :            :         /* When mlock fails, no memory will be locked, so we don't use munlock on free */
     116         [ #  # ]:          0 :         POSIX_GUARD(s2n_mem_free_no_mlock_impl(*ptr, *allocated));
     117         [ #  # ]:          0 :         POSIX_BAIL(S2N_ERR_MLOCK);
     118                 :          0 :     }
     119                 :            : 
     120 [ #  # ][ #  # ]:          0 :     POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
     121                 :            : 
     122                 :          0 :     return S2N_SUCCESS;
     123                 :          0 : }
     124                 :            : 
     125                 :            : static int s2n_mem_malloc_no_mlock_impl(void **ptr, uint32_t requested, uint32_t *allocated)
     126                 :    7702431 : {
     127                 :    7702431 :     *ptr = malloc(requested);
     128 [ #  # ][ -  + ]:    7702431 :     POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
     129                 :    7702431 :     *allocated = requested;
     130                 :            : 
     131                 :    7702431 :     return S2N_SUCCESS;
     132                 :    7702431 : }
     133                 :            : 
     134                 :            : int s2n_mem_set_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
     135                 :            :         s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
     136                 :          6 : {
     137 [ +  + ][ +  - ]:          6 :     POSIX_ENSURE(!initialized, S2N_ERR_INITIALIZED);
     138         [ -  + ]:          3 :     POSIX_GUARD_RESULT(s2n_mem_override_callbacks(mem_init_callback, mem_cleanup_callback,
     139                 :          3 :             mem_malloc_callback, mem_free_callback));
     140                 :          3 :     return S2N_SUCCESS;
     141                 :          3 : }
     142                 :            : 
     143                 :            : S2N_RESULT s2n_mem_override_callbacks(s2n_mem_init_callback mem_init_callback, s2n_mem_cleanup_callback mem_cleanup_callback,
     144                 :            :         s2n_mem_malloc_callback mem_malloc_callback, s2n_mem_free_callback mem_free_callback)
     145                 :        334 : {
     146 [ -  + ][ #  # ]:        334 :     RESULT_ENSURE_REF(mem_init_callback);
     147 [ -  + ][ #  # ]:        334 :     RESULT_ENSURE_REF(mem_cleanup_callback);
     148 [ -  + ][ #  # ]:        334 :     RESULT_ENSURE_REF(mem_malloc_callback);
     149 [ -  + ][ #  # ]:        334 :     RESULT_ENSURE_REF(mem_free_callback);
     150                 :            : 
     151                 :        334 :     s2n_mem_init_cb = mem_init_callback;
     152                 :        334 :     s2n_mem_cleanup_cb = mem_cleanup_callback;
     153                 :        334 :     s2n_mem_malloc_cb = mem_malloc_callback;
     154                 :        334 :     s2n_mem_free_cb = mem_free_callback;
     155                 :            : 
     156                 :        334 :     return S2N_RESULT_OK;
     157                 :        334 : }
     158                 :            : 
     159                 :            : S2N_RESULT s2n_mem_get_callbacks(s2n_mem_init_callback *mem_init_callback, s2n_mem_cleanup_callback *mem_cleanup_callback,
     160                 :            :         s2n_mem_malloc_callback *mem_malloc_callback, s2n_mem_free_callback *mem_free_callback)
     161                 :        330 : {
     162 [ -  + ][ #  # ]:        330 :     RESULT_ENSURE_REF(mem_init_callback);
     163 [ #  # ][ -  + ]:        330 :     RESULT_ENSURE_REF(mem_cleanup_callback);
     164 [ -  + ][ #  # ]:        330 :     RESULT_ENSURE_REF(mem_malloc_callback);
     165 [ -  + ][ #  # ]:        330 :     RESULT_ENSURE_REF(mem_free_callback);
     166                 :            : 
     167                 :        330 :     *mem_init_callback = s2n_mem_init_cb;
     168                 :        330 :     *mem_cleanup_callback = s2n_mem_cleanup_cb;
     169                 :        330 :     *mem_malloc_callback = s2n_mem_malloc_cb;
     170                 :        330 :     *mem_free_callback = s2n_mem_free_cb;
     171                 :            : 
     172                 :        330 :     return S2N_RESULT_OK;
     173                 :        330 : }
     174                 :            : 
     175                 :            : int s2n_alloc(struct s2n_blob *b, uint32_t size)
     176                 :    7833058 : {
     177 [ #  # ][ -  + ]:    7833058 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     178 [ -  + ][ #  # ]:    7833058 :     POSIX_ENSURE_REF(b);
     179                 :    7833058 :     const struct s2n_blob temp = { 0 };
     180                 :    7833058 :     *b = temp;
     181         [ -  + ]:    7833058 :     POSIX_GUARD(s2n_realloc(b, size));
     182                 :    7833058 :     return S2N_SUCCESS;
     183                 :    7833058 : }
     184                 :            : 
     185                 :            : /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
     186                 :            : bool s2n_blob_is_growable(const struct s2n_blob *b)
     187                 :   84470996 : {
     188 [ +  + ][ +  + ]:   84470996 :     return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
         [ +  + ][ +  - ]
                 [ +  - ]
     189                 :   84470996 : }
     190                 :            : 
     191                 :            : /* Tries to realloc the requested bytes.
     192                 :            :  * If successful, updates *b.
     193                 :            :  * If failed, *b remains unchanged
     194                 :            :  */
     195                 :            : int s2n_realloc(struct s2n_blob *b, uint32_t size)
     196                 :   18711834 : {
     197 [ #  # ][ -  + ]:   18711834 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     198 [ +  + ][ +  - ]:   18711834 :     POSIX_ENSURE_REF(b);
     199 [ +  + ][ +  - ]:   18711833 :     POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
     200         [ +  + ]:   18711832 :     if (size == 0) {
     201                 :   11004949 :         return s2n_free(b);
     202                 :   11004949 :     }
     203                 :            : 
     204                 :            :     /* blob already has space for the request */
     205         [ +  + ]:    7706883 :     if (size <= b->allocated) {
     206         [ +  + ]:       2227 :         if (size < b->size) {
     207                 :            :             /* Zero the existing blob memory before the we release it */
     208                 :         63 :             struct s2n_blob slice = { 0 };
     209         [ -  + ]:         63 :             POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
     210         [ -  + ]:         63 :             POSIX_GUARD(s2n_blob_zero(&slice));
     211                 :         63 :         }
     212                 :            : 
     213                 :       2227 :         b->size = size;
     214                 :       2227 :         return S2N_SUCCESS;
     215                 :       2227 :     }
     216                 :            : 
     217                 :    7704656 :     struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
     218         [ -  + ]:    7704656 :     if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
     219                 :          0 :         S2N_ERROR_PRESERVE_ERRNO();
     220                 :          0 :     }
     221                 :            : 
     222 [ -  + ][ #  # ]:    7704656 :     POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
     223 [ -  + ][ #  # ]:    7704656 :     POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
     224                 :            : 
     225         [ +  + ]:    7704656 :     if (b->size) {
     226 [ -  + ][ #  # ]:       9919 :         POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
                 [ +  - ]
     227                 :       9919 :     }
     228                 :            : 
     229         [ +  + ]:    7704656 :     if (b->allocated) {
     230         [ -  + ]:       9920 :         POSIX_GUARD(s2n_free(b));
     231                 :       9920 :     }
     232                 :            : 
     233                 :    7704656 :     *b = new_memory;
     234                 :    7704656 :     return S2N_SUCCESS;
     235                 :    7704656 : }
     236                 :            : 
     237                 :            : int s2n_free_object(uint8_t **p_data, uint32_t size)
     238                 :     667171 : {
     239 [ -  + ][ #  # ]:     667171 :     POSIX_ENSURE_REF(p_data);
     240                 :            : 
     241         [ +  + ]:     667171 :     if (*p_data == NULL) {
     242                 :       7320 :         return S2N_SUCCESS;
     243                 :       7320 :     }
     244                 :            : 
     245 [ -  + ][ #  # ]:     659851 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     246                 :     659851 :     struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
     247                 :            : 
     248                 :            :     /* s2n_free() will call free() even if it returns error (for a growable blob).
     249                 :            :     ** This makes sure *p_data is not used after free() */
     250                 :     659851 :     *p_data = NULL;
     251                 :            : 
     252                 :     659851 :     return s2n_free(&b);
     253                 :     659851 : }
     254                 :            : 
     255                 :            : int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
     256                 :      42838 : {
     257 [ -  + ][ #  # ]:      42838 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     258 [ #  # ][ -  + ]:      42838 :     POSIX_ENSURE_REF(to);
     259 [ -  + ][ #  # ]:      42838 :     POSIX_ENSURE_REF(from);
     260 [ -  + ][ #  # ]:      42838 :     POSIX_ENSURE_EQ(to->size, 0);
     261 [ -  + ][ #  # ]:      42838 :     POSIX_ENSURE_EQ(to->data, NULL);
     262 [ +  - ][ +  + ]:      42838 :     POSIX_ENSURE_NE(from->size, 0);
     263 [ #  # ][ -  + ]:      42836 :     POSIX_ENSURE_NE(from->data, NULL);
     264                 :            : 
     265         [ -  + ]:      42836 :     POSIX_GUARD(s2n_alloc(to, from->size));
     266                 :            : 
     267 [ #  # ][ -  + ]:      42836 :     POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
                 [ +  - ]
     268                 :            : 
     269                 :      42836 :     return S2N_SUCCESS;
     270                 :      42836 : }
     271                 :            : 
     272                 :            : int s2n_mem_init(void)
     273                 :        545 : {
     274 [ #  # ][ -  + ]:        545 :     POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     275                 :            : 
     276                 :        545 :     initialized = true;
     277                 :            : 
     278                 :        545 :     return S2N_SUCCESS;
     279                 :        545 : }
     280                 :            : 
     281                 :            : bool s2n_mem_is_init(void)
     282                 :          0 : {
     283                 :          0 :     return initialized;
     284                 :          0 : }
     285                 :            : 
     286                 :            : uint32_t s2n_mem_get_page_size(void)
     287                 :          0 : {
     288                 :          0 :     return page_size;
     289                 :          0 : }
     290                 :            : 
     291                 :            : int s2n_mem_cleanup(void)
     292                 :        545 : {
     293 [ -  + ][ #  # ]:        545 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     294 [ -  + ][ #  # ]:        545 :     POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     295                 :            : 
     296                 :        545 :     initialized = false;
     297                 :            : 
     298                 :        545 :     return S2N_SUCCESS;
     299                 :        545 : }
     300                 :            : 
     301                 :            : int s2n_free(struct s2n_blob *b)
     302                 :   65748415 : {
     303                 :            :     /* To avoid memory leaks, don't exit the function until the memory
     304                 :            :        has been freed */
     305                 :   65748415 :     int zero_rc = s2n_blob_zero(b);
     306         [ +  + ]:   65748415 :     POSIX_GUARD(s2n_free_without_wipe(b));
     307                 :   65748319 :     return zero_rc;
     308                 :   65748415 : }
     309                 :            : 
     310                 :            : int s2n_free_without_wipe(struct s2n_blob *b)
     311                 :   65759253 : {
     312 [ +  + ][ +  + ]:   65759253 :     POSIX_PRECONDITION(s2n_blob_validate(b));
     313                 :            : 
     314 [ +  + ][ +  - ]:   65759250 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     315 [ +  + ][ +  - ]:   65759157 :     POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
     316                 :            : 
     317         [ +  + ]:   65759156 :     if (b->data) {
     318 [ #  # ][ -  + ]:    7704656 :         POSIX_ENSURE(s2n_mem_free_cb(b->data, b->allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     319                 :    7704656 :     }
     320                 :            : 
     321                 :   65759156 :     *b = (struct s2n_blob){ 0 };
     322                 :            : 
     323                 :   65759156 :     return S2N_SUCCESS;
     324                 :   65759156 : }
     325                 :            : 
     326                 :            : int s2n_free_or_wipe(struct s2n_blob *b)
     327                 :   32028462 : {
     328 [ +  - ][ +  + ]:   32028462 :     POSIX_ENSURE_REF(b);
     329                 :   32028461 :     int zero_rc = s2n_blob_zero(b);
     330         [ +  + ]:   32028461 :     if (b->allocated) {
     331         [ -  + ]:      10824 :         POSIX_GUARD(s2n_free_without_wipe(b));
     332                 :      10824 :     }
     333                 :   32028461 :     return zero_rc;
     334                 :   32028461 : }

Generated by: LCOV version 1.14