Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers secure_allocator.c Source File

secure_allocator.c

00001 /*
00002  * Copyright (c) 2016, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #include "rtx_lib.h"
00019 
00020 /* uVisor uses rtx_memory instead of implementing its own dynamic,
00021  * non-fixed-size memory allocator. To do this, uVisor creates multiple
00022  * non-fixed-size allocator pools (one per page) and allocates memory from
00023  * these pools. uVisor must manage the memory for these pools' control blocks,
00024  * so it must know the size of these control blocks. */
00025 
00026 /* The following memory pool control block structs are copied from
00027  * rtx_memory.c, so that uVisor can manage the memory for these control blocks
00028  * within pages. */
00029 typedef struct mem_head_s {
00030   uint32_t size;                // Memory Pool size
00031   uint32_t used;                // Used Memory
00032 } mem_head_t;
00033 
00034 //  Memory Block Header structure
00035 typedef struct mem_block_s {
00036   struct mem_block_s *next;     // Next Memory Block in list
00037   uint32_t            info;     // Info: length = <31:2>:'00', type = <1:0>
00038 } mem_block_t;
00039 /* End copy */
00040 
00041 #include "secure_allocator.h"
00042 #include "uvisor-lib/uvisor-lib.h"
00043 
00044 #include <stdio.h>
00045 #include <stdlib.h>
00046 #include <string.h>
00047 
00048 /* Use printf with caution inside malloc: printf may allocate memory itself,
00049    so using printf in malloc may lead to recursive calls! */
00050 #define DPRINTF(...) {}
00051 
00052 /* offsetof is a gcc built-in function, this is the manual implementation */
00053 #define OFFSETOF(type, member) ((uint32_t) (&(((type *)(0))->member)))
00054 
00055 /* Internal structure currently only contains the page table. */
00056 typedef struct {
00057     UvisorPageTable table;
00058 } SecureAllocatorInternal;
00059 
00060 static inline UvisorPageTable * table(SecureAllocator allocator) {
00061     return &(((SecureAllocatorInternal *) allocator)->table);
00062 }
00063 
00064 SecureAllocator secure_allocator_create_with_pool(
00065     void * mem,
00066     size_t bytes)
00067 {
00068     SecureAllocatorInternal * allocator = mem;
00069     /* Signal that this is non-page allocated memory. */
00070     allocator->table.page_size = bytes;
00071     allocator->table.page_count = 0;
00072     /* The internal rtx_Memory memory pool structure must be placed AFTER
00073      * table.page_origins[0] !!! */
00074     size_t offset = OFFSETOF(SecureAllocatorInternal, table.page_origins) + sizeof(((UvisorPageTable) {0}).page_origins);
00075     uintptr_t page_origin = (uintptr_t) mem + offset;
00076 
00077     /* Align page_origin to a multiple of 8 (because RTX requries 8-byte
00078      * alignment of the origin). */
00079     page_origin = (page_origin + (0x8 - 1)) & -0x8;
00080     offset = page_origin - (uintptr_t) mem;
00081     size_t size = bytes - offset;
00082     /* Align size to a multiple of 8 (because RTX requires 8-byte alignment of
00083      * the size) */
00084     size &= -0x8;
00085 
00086     /* Create pool allocator structure inside the memory. */
00087     if (!osRtxMemoryInit((void *) page_origin, size)) {
00088         /* Abort if failed. */
00089         DPRINTF("secure_allocator_create_with_pool: pool allocator %p with offset %d creation failed (size %u bytes)\n\n", page_origin, offset, bytes - offset);
00090         return NULL;
00091     }
00092     /* Remember the pool allocator pointer though. */
00093     allocator->table.page_origins[0] = (void *) page_origin;
00094     DPRINTF("secure_allocator_create_with_pool: Created pool allocator %p with offset %d\n\n", page_origin, offset);
00095     return allocator;
00096 }
00097 
00098 SecureAllocator secure_allocator_create_with_pages(
00099     size_t size,
00100     size_t maximum_malloc_size)
00101 {
00102     const uint32_t page_size = uvisor_get_page_size();
00103     /* The rtx_Memory allocator puts one pool allocator structure at both the
00104      * beginning and end of the memory pool. */
00105     const size_t block_overhead = 2 * sizeof(mem_block_t);
00106     const size_t page_size_with_overhead = page_size + block_overhead;
00107     /* Calculate the integer part of required the page count. */
00108     size_t page_count = size / page_size_with_overhead;
00109     /* Add another page if the remainder is not zero. */
00110     if (size - page_count * page_size_with_overhead) {
00111         page_count++;
00112     }
00113     DPRINTF("secure_allocator_create_with_pages: Requesting %u pages for at least %uB\n", page_count, size);
00114 
00115     /* Compute the maximum allocation within our blocks. */
00116     size_t maximum_allocation_size = page_size - block_overhead;
00117     /* If the required maximum allocation is larger than we can provide, abort. */
00118     if (maximum_malloc_size > maximum_allocation_size) {
00119         DPRINTF("secure_allocator_create_with_pages: Maximum allocation request %uB is larger then available %uB\n\n", maximum_malloc_size, maximum_allocation_size);
00120         return NULL;
00121     }
00122 
00123     /* Compute the required memory size for the page table. */
00124     size_t allocator_type_size = sizeof(SecureAllocatorInternal);
00125     /* Add size for each additional page. */
00126     allocator_type_size += (page_count - 1) * sizeof(((UvisorPageTable) {0}).page_origins);
00127     /* Allocate this much memory. */
00128     SecureAllocatorInternal * const allocator = malloc(allocator_type_size);
00129     /* If malloc failed, abort. */
00130     if (allocator == NULL) {
00131         DPRINTF("secure_allocator_create_with_pages: SecureAllocatorInternal failed to be allocated!\n\n");
00132         return NULL;
00133     }
00134 
00135     /* Prepare the page table. */
00136     allocator->table.page_size = page_size;
00137     allocator->table.page_count = page_count;
00138     /* Get me some pages. */
00139     if (uvisor_page_malloc((UvisorPageTable *) &(allocator->table))) {
00140         free(allocator);
00141         DPRINTF("secure_allocator_create_with_pages: Not enough free pages available!\n\n");
00142         return NULL;
00143     }
00144 
00145     /* Initialize a memory pool structure in all pages. */
00146     for(size_t ii = 0; ii < page_count; ii++) {
00147         /* Add each page as a pool. */
00148         osStatus_t status = osRtxMemoryInit(allocator->table.page_origins[ii], page_size);
00149         if (status == osOK) {
00150           DPRINTF("secure_allocator_create_with_pages: Created memory pool allocator %p with offset %d page %u\n", allocator->table.page_origins[ii], 0, ii);
00151         } else {
00152           DPRINTF("secure_allocator_create_with_pages: Failed creating memory pool allocator %p with offset %d page %u\n", allocator->table.page_origins[ii], 0, ii);
00153         }
00154     }
00155     DPRINTF("\n");
00156     /* Aaaand across the line. */
00157     return (SecureAllocator) allocator;
00158 }
00159 
00160 int secure_allocator_destroy(
00161     SecureAllocator allocator)
00162 {
00163     DPRINTF("secure_allocator_destroy: Destroying memory pool allocator at %p\n", table(allocator)->page_origins[0]);
00164 
00165     /* Check if we are working on statically allocated memory. */
00166     SecureAllocatorInternal * alloc = (SecureAllocatorInternal * const) allocator;
00167     if (alloc->table.page_count == 0) {
00168         DPRINTF("secure_allocator_destroy: %p is not page-backed memory, not freeing!\n", allocator);
00169         return -1;
00170     }
00171 
00172     /* Free all pages. */
00173     if (uvisor_page_free(&(alloc->table))) {
00174         DPRINTF("secure_allocator_destroy: Unable to free pages!\n\n");
00175         return -1;
00176     }
00177 
00178     /* Free the allocator structure. */
00179     free(allocator);
00180 
00181     DPRINTF("\n");
00182     return 0;
00183 }
00184 
00185 void * secure_malloc(
00186     SecureAllocator allocator,
00187     size_t size)
00188 {
00189     size_t index = 0;
00190     do {
00191         /* Search in this page. */
00192         void * mem = osRtxMemoryAlloc(table(allocator)->page_origins[index], size, 0);
00193         /* Return if we found something. */
00194         if (mem) {
00195             DPRINTF("secure_malloc: Found %4uB in page %u at %p\n", size, index, mem);
00196             return mem;
00197         }
00198         /* Otherwise, go to the next page. */
00199         index++;
00200     } /* Continue search if more pages are available. */
00201     while (index < table(allocator)->page_count);
00202 
00203     DPRINTF("secure_malloc: Out of memory in allocator %p \n", allocator);
00204     /* We found nothing. */
00205     return NULL;
00206 }
00207 
00208 void * secure_aligned_alloc(
00209     SecureAllocator allocator,
00210     size_t alignment,
00211     size_t size)
00212 {
00213     /* Alignment must be a power of two! */
00214     if (alignment & ((1UL << ((31UL - __builtin_clz(alignment)) - 1)))) {
00215         return NULL;
00216     }
00217     /* TODO: THIS IS A NAIVE IMPLEMENTATION, which wastes much memory. */
00218     void * ptr = secure_malloc(allocator, size + alignment - 1);
00219     if (ptr == NULL) {
00220         return NULL;
00221     }
00222     return (void *) (((uint32_t) ptr + alignment - 1) & ~(alignment - 1));
00223 }
00224 
00225 void * secure_calloc(
00226     SecureAllocator allocator,
00227     size_t nmemb,
00228     size_t size)
00229 {
00230     if ((uint64_t) nmemb * size > SIZE_MAX) {
00231         /* (size * nmemb) has overflowed. */
00232         return NULL;
00233     }
00234     void * ptr = secure_malloc(allocator, size * nmemb);
00235     if (ptr == NULL) {
00236         return NULL;
00237     }
00238     memset(ptr, 0, size * nmemb);
00239     return ptr;
00240 }
00241 
00242 void * secure_realloc(
00243     SecureAllocator allocator,
00244     void * ptr,
00245     size_t new_size)
00246 {
00247     /* TODO: THIS IS A NAIVE IMPLEMENTATION, which always allocates new
00248        memory, and copies the memory, then frees the old memory. */
00249 
00250     /* Allocate new memory. */
00251     void * new_ptr = secure_malloc(allocator, new_size);
00252     /* If memory allocation failed, abort. */
00253     if (new_ptr == NULL) {
00254         return NULL;
00255     }
00256 
00257     /* Passing NULL as ptr is legal, realloc acts as malloc then. */
00258     if (ptr) {
00259         /* Get the size of the ptr memory. */
00260         size_t size = ((mem_block_t *) ((uint32_t) ptr - sizeof(mem_block_t)))->info & ~0x3;
00261         /* Copy the memory to the new location, min(new_size, size). */
00262         memcpy(new_ptr, ptr, new_size < size ? new_size : size);
00263         /* Free the previous memory. */
00264         secure_free(allocator, ptr);
00265     }
00266     return new_ptr;
00267 }
00268 
00269 void secure_free(
00270     SecureAllocator allocator,
00271     void * ptr)
00272 {
00273     size_t index = 0;
00274     do {
00275         /* Search in this page. */
00276         int ret = osRtxMemoryFree(table(allocator)->page_origins[index], ptr);
00277         /* Return if free was successful. */
00278         if (ret == 1) {
00279             DPRINTF("secure_free: Freed %p in page %u.\n", ptr, index);
00280             return;
00281         }
00282         /* Otherwise, go to the next page. */
00283         index++;
00284     } /* Continue search if more pages are available. */
00285     while (index < table(allocator)->page_count);
00286 
00287     DPRINTF("secure_free: %p not found in allocator %p!\n", ptr, allocator);
00288     /* We found nothing. */
00289     return;
00290 }