1 /*
2  * SPDX-FileCopyrightText: Copyright The TrustedFirmware-M Contributors
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  */
7 #include <inttypes.h>
8 #include <stdbool.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <assert.h>
12 #include "thread.h"
13 #include "psa/client.h"
14 #include "psa/service.h"
15 #include "internal_status_code.h"
16 #include "cmsis_compiler.h"
17 #include "utilities.h"
18 #include "lists.h"
19 #include "tfm_pools.h"
20 
21 /* Magic value that indicates a pool chunk has been allocated */
22 #define POOL_MAGIC_ALLOCATED UINT32_C(0xF0F0CCAA)
23 
tfm_pool_init(struct tfm_pool_instance_t * pool,size_t poolsz,size_t chunksz,size_t num)24 psa_status_t tfm_pool_init(struct tfm_pool_instance_t *pool, size_t poolsz,
25                            size_t chunksz, size_t num)
26 {
27     struct tfm_pool_chunk_t *pchunk;
28     size_t i;
29 
30     if (!pool || (num == 0)) {
31         return SPM_ERROR_BAD_PARAMETERS;
32     }
33 
34     /* Ensure buffer is large enough */
35     if (poolsz != (((chunksz + sizeof(struct tfm_pool_chunk_t)) * num) +
36         sizeof(struct tfm_pool_instance_t))) {
37         return SPM_ERROR_BAD_PARAMETERS;
38     }
39 
40     /* Buffer should be BSS cleared but clear it again */
41     spm_memset(pool, 0, poolsz);
42 
43     /* Chain pool chunks */
44     UNI_LIST_INIT_NODE(pool, next);
45 
46     pchunk = (struct tfm_pool_chunk_t *)pool->chunks;
47     for (i = 0; i < num; i++) {
48         UNI_LIST_INSERT_AFTER(pool, pchunk, next);
49         pchunk = (struct tfm_pool_chunk_t *)&pchunk->data[chunksz];
50     }
51 
52     /* Prepare instance and insert to pool list */
53     pool->chunksz = chunksz;
54     pool->pool_sz = poolsz;
55 
56     return PSA_SUCCESS;
57 }
58 
tfm_pool_alloc(struct tfm_pool_instance_t * pool)59 void *tfm_pool_alloc(struct tfm_pool_instance_t *pool)
60 {
61     struct tfm_pool_chunk_t *node;
62 
63     if (!pool) {
64         return NULL;
65     }
66 
67     if (UNI_LIST_IS_EMPTY(pool, next)) {
68         return NULL;
69     }
70 
71     node = UNI_LIST_NEXT_NODE(pool, next);
72     UNI_LIST_REMOVE_NODE(pool, node, next);
73 
74     node->magic = POOL_MAGIC_ALLOCATED;
75 
76     return &(node->data);
77 }
78 
tfm_pool_free(struct tfm_pool_instance_t * pool,void * ptr)79 void tfm_pool_free(struct tfm_pool_instance_t *pool, void *ptr)
80 {
81     struct tfm_pool_chunk_t *pchunk;
82 
83     /* In debug builds, trap invalid frees. */
84     assert(is_valid_chunk_data_in_pool(pool, ptr));
85 
86     pchunk = TO_CONTAINER(ptr, struct tfm_pool_chunk_t, data);
87 
88     pchunk->magic = 0;
89 
90     UNI_LIST_INSERT_AFTER(pool, pchunk, next);
91 
92     /* In debug builds, overwrite the data to catch use-after-free bugs. */
93 #ifndef NDEBUG
94     spm_memset(pchunk->data, 0xFF, pool->chunksz);
95 #endif
96 }
97 
is_valid_chunk_data_in_pool(struct tfm_pool_instance_t * pool,uint8_t * data)98 bool is_valid_chunk_data_in_pool(struct tfm_pool_instance_t *pool,
99                                  uint8_t *data)
100 {
101     const uintptr_t chunks_start = (uintptr_t)(pool->chunks);
102     const size_t chunks_offset = (uintptr_t)data - chunks_start;
103     struct tfm_pool_chunk_t *pchunk;
104 
105     /* Check that the message was allocated from the pool. */
106     if (((uintptr_t)data < chunks_start) || (chunks_offset >= pool->pool_sz)) {
107         return false;
108     }
109 
110     if ((chunks_offset % (pool->chunksz + sizeof(struct tfm_pool_chunk_t)))
111         != offsetof(struct tfm_pool_chunk_t, data)) {
112         return false;
113     }
114 
115     pchunk = TO_CONTAINER(data, struct tfm_pool_chunk_t, data);
116 
117     if (pchunk->magic != POOL_MAGIC_ALLOCATED) {
118         return false;
119     }
120 
121     return true;
122 }
123