509a8ada8cb3ab4167ece7a788f316f2647f31b3
[platform/core/security/tef-optee_os.git] / lib / libutee / tee_user_mem.c
1 /*
2  * Copyright (c) 2014, STMicroelectronics International N.V.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <assert.h>
29 #include <inttypes.h>
30 #include <string.h>
31 #include <compiler.h>
32 #include <utee_defines.h>
33 #include <sys/queue.h>
34 #include <tee_api.h>
35 #include <util.h>
36 #include "tee_user_mem.h"
37 #include "utee_misc.h"
38
39 #ifdef CFG_NO_USER_MALLOC_GARBAGE
40
41 void *tee_user_mem_alloc(size_t len, uint32_t hint)
42 {
43         uint8_t *p;
44
45         switch (hint) {
46         case TEE_MALLOC_FILL_ZERO:
47         case TEE_USER_MEM_HINT_NO_FILL_ZERO:
48                 break;
49         default:
50                 EMSG("Invalid alloc hint [%X]", (unsigned int)hint);
51                 return NULL;
52         }
53
54         p = utee_malloc(len);
55         if (p == NULL)
56                 return NULL;
57
58         if (hint == TEE_MALLOC_FILL_ZERO)
59                 memset(p, 0, len);
60 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
61         if (hint == (typeof(hint)) TEE_USER_MEM_HINT_NO_FILL_ZERO)
62                 memset(p, 0xBB, len);
63 #endif
64         return p;
65 }
66
67 void *tee_user_mem_realloc(void *buffer, size_t len)
68 {
69         return utee_realloc(buffer, len);
70 }
71
72 void tee_user_mem_free(void *buffer)
73 {
74         utee_free(buffer);
75 }
76
77 void tee_user_mem_mark_heap(void)
78 {
79 }
80
81 size_t tee_user_mem_check_heap(void)
82 {
83         return 0;
84 }
85
86 #else /* CFG_NO_USER_MALLOC_GARBAGE */
87
88 /*
89  * Manage and track the memory allocation in the libc heap of the user side (TA)
90  * Register all allocations and the current TA Provide a garbage api to delete
91  * all allocations of a given TA.
92  */
93
94 /*
95  * ARTIST is a magic number to be compliant to a allocation/free of 0 size.
96  */
97 static const void *ARTIST = (void *)0x10;
98
99 /*
100  * Link list definition for tracking the memory activity.
101  */
102 struct user_mem_elem {
103         TAILQ_ENTRY(user_mem_elem) link;
104         size_t len;
105         uint32_t hint;
106 };
107 TAILQ_HEAD(user_mem_head, user_mem_elem) user_mem_head =
108 TAILQ_HEAD_INITIALIZER(user_mem_head);
109
110 /*
111  * Debug tools.
112  */
113 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
114 struct tee_user_mem_stats {
115         int nb_alloc;
116         size_t size;
117 };
118 static void tee_user_mem_status(struct tee_user_mem_stats *stats);
119
120 /* Extra size of memory to add canary line check */
121 static const size_t CANARY_LINE_SIZE = 1;
122 #else
123 static const size_t CANARY_LINE_SIZE;
124 #endif
125
126 /*
127  * Accessors from an element of the list and its attribute.
128  */
129 static inline void *buf_addr(const struct user_mem_elem *e)
130 {
131         return (uint8_t *)e + sizeof(struct user_mem_elem);
132 }
133
134 static inline size_t buf_size(const struct user_mem_elem *e)
135 {
136         return e->len - sizeof(struct user_mem_elem) - CANARY_LINE_SIZE;
137 }
138
139 static inline void *elem_addr(const void *buffer)
140 {
141         return (uint8_t *)buffer - sizeof(struct user_mem_elem);
142 }
143
144 /*
145  * Check if a given buffer address has been allocated with this tool.
146  */
147 static int is_buffer_valid(void *buffer)
148 {
149         struct user_mem_elem *e;
150
151         TAILQ_FOREACH(e, &user_mem_head, link) {
152                 if (buf_addr(e) == buffer)
153                         return 1;
154         }
155         return 0;
156 }
157
158 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
159 /*
160  * Common print of an element.
161  */
162 #if (TRACE_LEVEL > 0)
163 static void print_buf(int tl, const char *func, int line, const char *prefix,
164                       const struct user_mem_elem *e)
165 {
166         trace_printf(NULL, 0, tl, true,
167                     "%s:%d: %slink:[%p], buf:[%p:%zu]\n",
168                     func, line, prefix, (void *)e, buf_addr(e), buf_size(e));
169 }
170
171 #define PB(trace_level, prefix, elem) { print_buf(trace_level, __func__, \
172                                           __LINE__, prefix, elem); }
173 #else
174 #define PB(trace_level, prefix, elem) (void)0
175 #endif /* TRACE_LEVEL */
176
177 /*
178  * Heap mark to track leak.
179  *
180  * Can't use OS21 partition api to be compatible with TZ.
181  *
182  * Can't use generic mallinfo to dump the libc heap because the tee core
183  * use also this heap.
184  *
185  * So use a simple static var which is updated on tee_user_mem_ operations.
186  */
187 static size_t heap_level;
188
189 /*
190  * global stats to summarize memory activities cross TA's.
191  */
192 static struct tee_user_mem_stats global_stats;
193
194 static void heap_inc(size_t size)
195 {
196         INMSG("%zu", size);
197         heap_level += size;
198
199         global_stats.nb_alloc++;
200         global_stats.size += size;
201         OUTMSG("%zu", global_stats.size);
202 }
203
204 static void heap_dec(size_t size)
205 {
206         INMSG("%zu %zu", heap_level, size);
207         heap_level -= size;
208
209         global_stats.nb_alloc--;
210         global_stats.size -= size;
211         OUTMSG("%zu", global_stats.size);
212 }
213
214 /*
215  * Check integrity of the buffer and the list.
216  */
217 static int check_elem_end(struct user_mem_elem *e)
218 {
219         uint8_t *cp = (uint8_t *)e;
220
221         /*
222          * The following check detects storing off the end of the allocated
223          * space in the buffer by comparing the end of buffer checksum with the
224          * address of the buffer.
225          */
226         if ((cp[e->len - CANARY_LINE_SIZE] !=
227              ((((uintptr_t) cp) & 0xFF) ^ 0xC5))) {
228                 PB(TRACE_ERROR, "Corrupted: ", e);
229                 return 0;
230         }
231
232         return 1;
233 }
234
235 static int check_elem(struct user_mem_elem *ap)
236 {
237         struct user_mem_elem *e;
238
239         /* Validate queue links */
240         if (!ap)
241                 return 0;
242
243         if ((uintptr_t)ap & 0x3) {
244                 EMSG("corrupted allocations");
245                 TEE_Panic(0);
246         }
247
248         e = TAILQ_NEXT(ap, link);
249         if (e != NULL && TAILQ_PREV(e, user_mem_head, link) != ap) {
250                 PB(TRACE_ERROR, "Orphaned: ", e);
251                 return 0;
252         }
253
254         e = TAILQ_PREV(ap, user_mem_head, link);
255         if (e != NULL && TAILQ_NEXT(e, link) != ap) {
256                 PB(TRACE_ERROR, "Orphaned: ", e);
257                 return 0;
258         }
259
260         return check_elem_end(ap);
261 }
262
263 /* In debug mode, trap PC element are corrupted. */
264 static int is_mem_coherent(void)
265 {
266         struct user_mem_elem *e;
267
268         TAILQ_FOREACH(e, &user_mem_head, link) {
269                 if (!check_elem(e)) {
270                         assert(0);
271                         return 0;
272                 }
273         }
274         return 1;
275 }
276
277 #else /* CFG_TEE_CORE_USER_MEM_DEBUG */
278 static void heap_inc(size_t size  __unused)
279 {
280 }
281
282 static void heap_dec(size_t size  __unused)
283 {
284 }
285
286 #define PB(trace_level, prefix, elem) do {} while (0)
287 #endif /* CFG_TEE_CORE_USER_MEM_DEBUG */
288
289 /*
290  *  API methods
291  */
292
293 /*
294  * Allocate buffer, enqueing on the orphaned buffer tracking list.
295  */
296 void *tee_user_mem_alloc(size_t len, uint32_t hint)
297 {
298         uint8_t *cp;
299         void *buf = NULL;
300         size_t total_len =
301             len + sizeof(struct user_mem_elem) + CANARY_LINE_SIZE;
302
303
304         INMSG("%zu 0x%" PRIx32, len, hint);
305
306         if ((int)len < 0) {
307                 OUTMSG("0x0");
308                 return NULL;
309         }
310
311         if (len == 0) {
312                 OUTMSG("%p", ARTIST);
313                 return (void *)ARTIST;
314         }
315
316         /* Check hint */
317         switch (hint) {
318         case TEE_MALLOC_FILL_ZERO:
319         case TEE_USER_MEM_HINT_NO_FILL_ZERO:
320                 break;
321         default:
322                 EMSG("Invalid alloc hint [0x%" PRIx32 "]", hint);
323                 OUTMSG("0x0");
324                 return NULL;
325         }
326
327         cp = utee_malloc(total_len);
328         if (cp != NULL) {
329                 struct user_mem_elem *e = (struct user_mem_elem *)(void *)cp;
330                 e->len = total_len;
331                 e->hint = hint;
332                 heap_inc(total_len);
333
334                 /* Enqueue buffer on allocated list */
335                 TAILQ_INSERT_TAIL(&user_mem_head, e, link);
336
337 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
338                 /* Emplace end-clobber detector at end of buffer */
339                 cp[total_len - CANARY_LINE_SIZE] =
340                     (((uintptr_t) cp) & 0xFF) ^ 0xC5;
341 #endif
342
343                 PB(TRACE_DEBUG, "Allocate: ", (void *)e);
344
345                 buf = buf_addr(e);
346
347                 if (hint == TEE_MALLOC_FILL_ZERO)
348                         memset(buf, 0, len);
349 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
350                 else if (hint == (typeof(hint)) TEE_USER_MEM_HINT_NO_FILL_ZERO)
351                         /* Fill buffer with init pattern */
352                         memset(buf, 0xBB, len);
353 #endif
354         }
355
356         OUTMSG("[%p]", buf);
357         return buf;
358 }
359
360 /*
361  * Adjust the size of a previously allocated buffer. Because of the need to
362  * maintain our control storage, tee_user_mem_realloc() must always allocate a
363  * new block and copy the data in the old block. This may result in programs
364  * which make heavy use of realloc() running much slower than normally.
365  */
366 void *tee_user_mem_realloc(void *buffer, size_t len)
367 {
368         size_t olen;
369         void *buf;
370         struct user_mem_elem *e;
371
372         INMSG("[%p:%d]", buffer, (int)len);
373
374         if ((int)len < 0) {
375                 OUTMSG("0x0");
376                 return NULL;
377         }
378
379         /* If the old block pointer
380          *  - is NULL,
381          *  - or was allocated with a zero size,
382          *  - or invalid buffer
383          * treat realloc() as a malloc().  */
384         if (buffer == NULL || buffer == ARTIST || !is_buffer_valid(buffer)) {
385                 buf = tee_user_mem_alloc(len, DEFAULT_TEE_MALLOC_HINT);
386                 OUTMSG("%p", buf);
387                 return buf;
388         }
389
390         /*
391          * If the old and new sizes are the same, be a nice guy and just return
392          * the buffer passed in.
393          */
394         e = (struct user_mem_elem *)elem_addr(buffer);
395         olen = buf_size(e);
396         if (len == olen) {
397                 OUTMSG("[%p]", buffer);
398                 return buffer;
399         }
400
401         /*
402          * Sizes differ. Allocate a new buffer of the requested size. If we
403          * can't obtain such a buffer, return NULL from realloc() and leave the
404          * buffer in ptr intact.
405          */
406         buf = tee_user_mem_alloc(len, e->hint);
407         if (buf != NULL) {
408                 memcpy(buf, buffer, MIN(len, olen));
409
410                 /* All done.  Free and dechain the original buffer. */
411                 tee_user_mem_free(buffer);
412         }
413
414         OUTMSG("[%p]", buf);
415         return buf;
416 }
417
418 /*
419  * Update free pool availability. free is never called except through this
420  * interface. free(x) is defined to generate a call to this routine.
421  */
422 void tee_user_mem_free(void *buffer)
423 {
424         uint8_t *cp;
425         struct user_mem_elem *e;
426
427         INMSG("[%p]", buffer);
428
429         /* It is OK to free NULL */
430         if (buffer == NULL || buffer == ARTIST)
431                 return;
432
433         /* Check if the buffer is valid */
434         if (!is_buffer_valid(buffer)) {
435                 EMSG("unknown freed buffer [%p]", buffer);
436                 return;
437         }
438
439         cp = elem_addr(buffer);
440         e = (struct user_mem_elem *)(void *)cp;
441
442         PB(TRACE_DEBUG, "Free: ", (void *)e);
443
444 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
445         if (!check_elem(e)) {
446                 EMSG("corrupted allocation");
447                 TEE_Panic(0);
448         }
449 #endif
450
451         TAILQ_REMOVE(&user_mem_head, e, link);
452
453         heap_dec(e->len);
454
455 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
456         /*
457          * Now we wipe the contents of the just-released buffer with "designer
458          * garbage" (Duff  Kurland's  phrase) of alternating bits.  This is
459          * intended to ruin the day for any miscreant who attempts to access
460          * data through a pointer into storage that's been previously released.
461          */
462         memset(cp, 0xAA, e->len);
463 #endif
464
465         utee_free(cp);
466
467         OUTMSG();
468 }
469
470 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
471 /*
472  * Accessors to mark the heap.
473  */
474 void tee_user_mem_mark_heap(void)
475 {
476         INMSG();
477         /* Reset the marker */
478         heap_level = 0;
479         OUTMSG();
480 }
481
482 /*
483  * Accessors to check the heap and the whole list.
484  * Return 0 means no leak and link list is valid.
485  * Return >0 return nb bytes of leak.
486  */
487 size_t tee_user_mem_check_heap(void)
488 {
489         int res = 0;
490         INMSG("%zu", heap_level);
491
492         if (heap_level) {
493                 EMSG("ta heap has changed of [%zu]", heap_level);
494                 OUTMSG("%zu", heap_level);
495                 return heap_level;
496         }
497
498         res = !is_mem_coherent();
499
500         OUTMSG("%d", res);
501         return res;
502 }
503
504 /*
505  * Dump the stats and elements of the memory activity.
506  */
507 void tee_user_mem_status(struct tee_user_mem_stats *stats)
508 {
509         struct user_mem_elem *e;
510         if (stats != NULL)
511                 memcpy(stats, &global_stats, sizeof(struct tee_user_mem_stats));
512
513         if (global_stats.nb_alloc > 0) {
514                 IMSG("Nb alloc:\t[%d]", global_stats.nb_alloc);
515                 IMSG("Size:\t[%zu]", global_stats.size);
516         }
517
518         TAILQ_FOREACH(e, &user_mem_head, link) {
519                 PB(TRACE_ERROR, "", e);
520         }
521 }
522 #else
523 void tee_user_mem_mark_heap(void)
524 {
525 }
526
527 size_t tee_user_mem_check_heap(void)
528 {
529         return 0;
530 }
531 #endif /* CFG_TEE_CORE_USER_MEM_DEBUG */
532
533 /*
534  * Free memory allocated from a specific TA.
535  */
536 void tee_user_mem_garbage(void)
537 {
538 #if (CFG_TEE_CORE_USER_MEM_DEBUG == 1)
539         tee_user_mem_status(NULL);
540 #endif
541
542         while (TAILQ_FIRST(&user_mem_head) != NULL)
543                 tee_user_mem_free(buf_addr(TAILQ_FIRST(&user_mem_head)));
544 }
545
546 #endif /* CFG_NO_USER_MALLOC_GARBAGE */