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-11-15 08:28:27 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                 :        546 : {
      50                 :        546 :     long sysconf_rc = sysconf(_SC_PAGESIZE);
      51                 :            : 
      52                 :            :     /* sysconf must not error, and page_size cannot be 0 */
      53 [ -  + ][ #  # ]:        546 :     POSIX_ENSURE_GT(sysconf_rc, 0);
      54                 :            : 
      55                 :            :     /* page_size must be a valid uint32 */
      56                 :        546 :     long max_page_size = MIN(UINT32_MAX, LONG_MAX);
      57 [ -  + ][ #  # ]:        546 :     POSIX_ENSURE_LTE(sysconf_rc, max_page_size);
      58                 :        546 :     page_size = (uint32_t) sysconf_rc;
      59                 :            : 
      60 [ +  - ][ #  # ]:        546 :     if (getenv("S2N_DONT_MLOCK") || s2n_in_unit_test()) {
      61                 :        546 :         s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
      62                 :        546 :         s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
      63                 :        546 :     }
      64                 :        546 :     return S2N_SUCCESS;
      65                 :        546 : }
      66                 :            : 
      67                 :            : static int s2n_mem_cleanup_impl(void)
      68                 :        546 : {
      69                 :        546 :     page_size = 4096;
      70                 :        546 :     s2n_mem_malloc_cb = s2n_mem_malloc_no_mlock_impl;
      71                 :        546 :     s2n_mem_free_cb = s2n_mem_free_no_mlock_impl;
      72                 :        546 :     return S2N_SUCCESS;
      73                 :        546 : }
      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                 :    7719171 : {
      85                 :    7719171 :     free(ptr);
      86                 :            : 
      87                 :    7719171 :     return S2N_SUCCESS;
      88                 :    7719171 : }
      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                 :    7719171 : {
     127                 :    7719171 :     *ptr = malloc(requested);
     128 [ #  # ][ -  + ]:    7719171 :     POSIX_ENSURE(*ptr != NULL, S2N_ERR_ALLOC);
     129                 :    7719171 :     *allocated = requested;
     130                 :            : 
     131                 :    7719171 :     return S2N_SUCCESS;
     132                 :    7719171 : }
     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                 :            : /**
     176                 :            :  * Allocate a new blob on the heap.
     177                 :            :  * 
     178                 :            :  * The blob will be _growable_.
     179                 :            :  * 
     180                 :            :  * This blob owns the underlying memory, which will be freed when `s2n_free` is 
     181                 :            :  * called on `b`.
     182                 :            :  */
     183                 :            : int s2n_alloc(struct s2n_blob *b, uint32_t size)
     184                 :    7849890 : {
     185 [ #  # ][ -  + ]:    7849890 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     186 [ -  + ][ #  # ]:    7849890 :     POSIX_ENSURE_REF(b);
     187                 :    7849890 :     const struct s2n_blob temp = { 0 };
     188                 :    7849890 :     *b = temp;
     189         [ -  + ]:    7849890 :     POSIX_GUARD(s2n_realloc(b, size));
     190                 :    7849890 :     return S2N_SUCCESS;
     191                 :    7849890 : }
     192                 :            : 
     193                 :            : /* A blob is growable if it is either explicitly marked as such, or if it contains no data */
     194                 :            : bool s2n_blob_is_growable(const struct s2n_blob *b)
     195                 :   84569461 : {
     196 [ +  + ][ +  + ]:   84569461 :     return b && (b->growable || (b->data == NULL && b->size == 0 && b->allocated == 0));
         [ +  + ][ +  - ]
                 [ +  - ]
     197                 :   84569461 : }
     198                 :            : 
     199                 :            : /**
     200                 :            :  * Resize a blob to `size`. The blob must be allocated or empty.
     201                 :            :  * 
     202                 :            :  * This will allocate more memory if necessary, or reuse the existing allocation
     203                 :            :  * if the requested size is smaller than the current size.
     204                 :            :  *
     205                 :            :  * If failed, *b remains unchanged.
     206                 :            :  */
     207                 :            : int s2n_realloc(struct s2n_blob *b, uint32_t size)
     208                 :   18738065 : {
     209 [ #  # ][ -  + ]:   18738065 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     210 [ +  + ][ +  - ]:   18738065 :     POSIX_ENSURE_REF(b);
     211 [ +  + ][ +  - ]:   18738064 :     POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_RESIZE_STATIC_BLOB);
     212         [ +  + ]:   18738063 :     if (size == 0) {
     213                 :   11014440 :         return s2n_free(b);
     214                 :   11014440 :     }
     215                 :            : 
     216                 :            :     /* blob already has space for the request */
     217         [ +  + ]:    7723623 :     if (size <= b->allocated) {
     218         [ +  + ]:       2227 :         if (size < b->size) {
     219                 :            :             /* Zero the existing blob memory before the we release it */
     220                 :         63 :             struct s2n_blob slice = { 0 };
     221         [ -  + ]:         63 :             POSIX_GUARD(s2n_blob_slice(b, &slice, size, b->size - size));
     222         [ -  + ]:         63 :             POSIX_GUARD(s2n_blob_zero(&slice));
     223                 :         63 :         }
     224                 :            : 
     225                 :       2227 :         b->size = size;
     226                 :       2227 :         return S2N_SUCCESS;
     227                 :       2227 :     }
     228                 :            : 
     229                 :    7721396 :     struct s2n_blob new_memory = { .data = NULL, .size = size, .allocated = 0, .growable = 1 };
     230         [ -  + ]:    7721396 :     if (s2n_mem_malloc_cb((void **) &new_memory.data, new_memory.size, &new_memory.allocated) != 0) {
     231                 :          0 :         S2N_ERROR_PRESERVE_ERRNO();
     232                 :          0 :     }
     233                 :            : 
     234 [ -  + ][ #  # ]:    7721396 :     POSIX_ENSURE(new_memory.allocated >= new_memory.size, S2N_ERR_ALLOC);
     235 [ -  + ][ #  # ]:    7721396 :     POSIX_ENSURE(new_memory.data != NULL, S2N_ERR_ALLOC);
     236                 :            : 
     237         [ +  + ]:    7721396 :     if (b->size) {
     238 [ -  + ][ #  # ]:      10056 :         POSIX_CHECKED_MEMCPY(new_memory.data, b->data, b->size);
                 [ +  - ]
     239                 :      10056 :     }
     240                 :            : 
     241         [ +  + ]:    7721396 :     if (b->allocated) {
     242         [ -  + ]:      10057 :         POSIX_GUARD(s2n_free(b));
     243                 :      10057 :     }
     244                 :            : 
     245                 :    7721396 :     *b = new_memory;
     246                 :    7721396 :     return S2N_SUCCESS;
     247                 :    7721396 : }
     248                 :            : 
     249                 :            : int s2n_free_object(uint8_t **p_data, uint32_t size)
     250                 :     678620 : {
     251 [ -  + ][ #  # ]:     678620 :     POSIX_ENSURE_REF(p_data);
     252                 :            : 
     253         [ +  + ]:     678620 :     if (*p_data == NULL) {
     254                 :       8496 :         return S2N_SUCCESS;
     255                 :       8496 :     }
     256                 :            : 
     257 [ -  + ][ #  # ]:     670124 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     258                 :     670124 :     struct s2n_blob b = { .data = *p_data, .allocated = size, .size = size, .growable = 1 };
     259                 :            : 
     260                 :            :     /* s2n_free() will call free() even if it returns error (for a growable blob).
     261                 :            :     ** This makes sure *p_data is not used after free() */
     262                 :     670124 :     *p_data = NULL;
     263                 :            : 
     264                 :     670124 :     return s2n_free(&b);
     265                 :     670124 : }
     266                 :            : 
     267                 :            : /**
     268                 :            :  * Allocate enough memory for `to` to contain all the data in `from`, then copy
     269                 :            :  * the data in `from` to `to`.
     270                 :            :  */
     271                 :            : int s2n_dup(struct s2n_blob *from, struct s2n_blob *to)
     272                 :      43495 : {
     273 [ -  + ][ #  # ]:      43495 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     274 [ #  # ][ -  + ]:      43495 :     POSIX_ENSURE_REF(to);
     275 [ -  + ][ #  # ]:      43495 :     POSIX_ENSURE_REF(from);
     276 [ -  + ][ #  # ]:      43495 :     POSIX_ENSURE_EQ(to->size, 0);
     277 [ -  + ][ #  # ]:      43495 :     POSIX_ENSURE_EQ(to->data, NULL);
     278 [ +  - ][ +  + ]:      43495 :     POSIX_ENSURE_NE(from->size, 0);
     279 [ #  # ][ -  + ]:      43493 :     POSIX_ENSURE_NE(from->data, NULL);
     280                 :            : 
     281         [ -  + ]:      43493 :     POSIX_GUARD(s2n_alloc(to, from->size));
     282                 :            : 
     283 [ #  # ][ -  + ]:      43493 :     POSIX_CHECKED_MEMCPY(to->data, from->data, to->size);
                 [ +  - ]
     284                 :            : 
     285                 :      43493 :     return S2N_SUCCESS;
     286                 :      43493 : }
     287                 :            : 
     288                 :            : int s2n_mem_init(void)
     289                 :        549 : {
     290 [ #  # ][ -  + ]:        549 :     POSIX_ENSURE(s2n_mem_init_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     291                 :            : 
     292                 :        549 :     initialized = true;
     293                 :            : 
     294                 :        549 :     return S2N_SUCCESS;
     295                 :        549 : }
     296                 :            : 
     297                 :            : bool s2n_mem_is_init(void)
     298                 :          0 : {
     299                 :          0 :     return initialized;
     300                 :          0 : }
     301                 :            : 
     302                 :            : uint32_t s2n_mem_get_page_size(void)
     303                 :          0 : {
     304                 :          0 :     return page_size;
     305                 :          0 : }
     306                 :            : 
     307                 :            : int s2n_mem_cleanup(void)
     308                 :        549 : {
     309 [ -  + ][ #  # ]:        549 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     310 [ -  + ][ #  # ]:        549 :     POSIX_ENSURE(s2n_mem_cleanup_cb() >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     311                 :            : 
     312                 :        549 :     initialized = false;
     313                 :            : 
     314                 :        549 :     return S2N_SUCCESS;
     315                 :        549 : }
     316                 :            : 
     317                 :            : int s2n_free(struct s2n_blob *b)
     318                 :   65820059 : {
     319                 :            :     /* To avoid memory leaks, don't exit the function until the memory
     320                 :            :        has been freed */
     321                 :   65820059 :     int zero_rc = s2n_blob_zero(b);
     322         [ +  + ]:   65820059 :     POSIX_GUARD(s2n_free_without_wipe(b));
     323                 :   65819963 :     return zero_rc;
     324                 :   65820059 : }
     325                 :            : 
     326                 :            : int s2n_free_without_wipe(struct s2n_blob *b)
     327                 :   65831487 : {
     328 [ +  + ][ +  + ]:   65831487 :     POSIX_PRECONDITION(s2n_blob_validate(b));
     329                 :            : 
     330 [ +  + ][ +  - ]:   65831484 :     POSIX_ENSURE(initialized, S2N_ERR_NOT_INITIALIZED);
     331 [ +  + ][ +  - ]:   65831391 :     POSIX_ENSURE(s2n_blob_is_growable(b), S2N_ERR_FREE_STATIC_BLOB);
     332                 :            : 
     333         [ +  + ]:   65831390 :     if (b->data) {
     334 [ #  # ][ -  + ]:    7721396 :         POSIX_ENSURE(s2n_mem_free_cb(b->data, b->allocated) >= S2N_SUCCESS, S2N_ERR_CANCELLED);
     335                 :    7721396 :     }
     336                 :            : 
     337                 :   65831390 :     *b = (struct s2n_blob){ 0 };
     338                 :            : 
     339                 :   65831390 :     return S2N_SUCCESS;
     340                 :   65831390 : }
     341                 :            : 
     342                 :            : int s2n_free_or_wipe(struct s2n_blob *b)
     343                 :   32056206 : {
     344 [ +  - ][ +  + ]:   32056206 :     POSIX_ENSURE_REF(b);
     345                 :   32056205 :     int zero_rc = s2n_blob_zero(b);
     346         [ +  + ]:   32056205 :     if (b->allocated) {
     347         [ -  + ]:      11414 :         POSIX_GUARD(s2n_free_without_wipe(b));
     348                 :      11414 :     }
     349                 :   32056205 :     return zero_rc;
     350                 :   32056205 : }

Generated by: LCOV version 1.14