1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017, 2019, Linaro Limited 4 * Copyright (c) 2020, Arm Limited. 5 */ 6 7 /* 8 * Security properties of REE-FS TAs 9 * ================================= 10 * 11 * Authentication only 12 * ------------------- 13 * 14 * Required security properties: 15 * 1. Authentication and non-repudiation of a TA to Service Provider (SP). 16 * 2. Integrity of a TA. 17 * 18 * To satisfy (1) and (2), SP needs to sign TA and OP-TEE core needs to verify 19 * the signature using SP public key with computed hash of the TA. 20 * 21 * Authentication along with Confidentiality 22 * ----------------------------------------- 23 * 24 * Required security properties: 25 * 1. Authentication and non-repudiation of a TA to Service Provider (SP). 26 * 2. Confidentiality of a TA. 27 * 3. Integrity of an encrypted TA blob. 28 * 29 * To satisfy (1), SP needs to sign plain TA and OP-TEE core needs to verify the 30 * signature using SP public key with computed hash of the TA. 31 * 32 * To satisfy (2) and (3), SP needs to do authenticated encryption of TA and 33 * OP-TEE core needs to do authenticated decryption of TA to retrieve its 34 * contents. Here encryption provides the confidentiality of TA and MAC tag 35 * provides the integrity of encrypted TA blob. 36 */ 37 38 #include <assert.h> 39 #include <crypto/crypto.h> 40 #include <initcall.h> 41 #include <kernel/thread.h> 42 #include <kernel/ts_store.h> 43 #include <mm/core_memprot.h> 44 #include <mm/tee_mm.h> 45 #include <mm/mobj.h> 46 #include <optee_rpc_cmd.h> 47 #include <signed_hdr.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <tee_api_defines_extensions.h> 51 #include <tee_api_types.h> 52 #include <tee/tee_pobj.h> 53 #include <tee/tee_svc_storage.h> 54 #include <tee/tee_ta_enc_manager.h> 55 #include <tee/uuid.h> 56 #include <utee_defines.h> 57 58 struct ree_fs_ta_handle { 59 struct shdr *nw_ta; /* Non-secure (shared memory) */ 60 size_t nw_ta_size; 61 struct mobj *mobj; 62 size_t offs; 63 struct shdr *shdr; /* Verified secure copy of @nw_ta's signed header */ 64 void *hash_ctx; 65 void *enc_ctx; 66 struct shdr_bootstrap_ta *bs_hdr; 67 struct shdr_encrypted_ta *ehdr; 68 }; 69 70 struct ver_db_entry { 71 uint8_t uuid[sizeof(TEE_UUID)]; 72 uint32_t version; 73 }; 74 75 struct ver_db_hdr { 76 uint32_t db_version; 77 uint32_t nb_entries; 78 }; 79 80 static const char ta_ver_db[] = "ta_ver.db"; 81 static struct mutex ver_db_mutex = MUTEX_INITIALIZER; 82 83 /* 84 * Load a TA via RPC with UUID defined by input param @uuid. The virtual 85 * address of the raw TA binary is received in out parameter @ta. 86 */ 87 static TEE_Result rpc_load(const TEE_UUID *uuid, struct shdr **ta, 88 size_t *ta_size, struct mobj **mobj) 89 { 90 TEE_Result res; 91 struct thread_param params[2]; 92 93 if (!uuid || !ta || !mobj || !ta_size) 94 return TEE_ERROR_BAD_PARAMETERS; 95 96 memset(params, 0, sizeof(params)); 97 params[0].attr = THREAD_PARAM_ATTR_VALUE_IN; 98 tee_uuid_to_octets((void *)¶ms[0].u.value, uuid); 99 params[1].attr = THREAD_PARAM_ATTR_MEMREF_OUT; 100 101 res = thread_rpc_cmd(OPTEE_RPC_CMD_LOAD_TA, 2, params); 102 if (res != TEE_SUCCESS) 103 return res; 104 105 *mobj = thread_rpc_alloc_payload(params[1].u.memref.size); 106 if (!*mobj) 107 return TEE_ERROR_OUT_OF_MEMORY; 108 109 *ta = mobj_get_va(*mobj, 0, params[1].u.memref.size); 110 if (!*ta) { 111 res = TEE_ERROR_SHORT_BUFFER; 112 goto exit; 113 } 114 /* We don't expect NULL as thread_rpc_alloc_payload() was successful */ 115 assert(*ta); 116 *ta_size = params[1].u.memref.size; 117 118 params[0].attr = THREAD_PARAM_ATTR_VALUE_IN; 119 tee_uuid_to_octets((void *)¶ms[0].u.value, uuid); 120 params[1].attr = THREAD_PARAM_ATTR_MEMREF_OUT; 121 params[1].u.memref.offs = 0; 122 params[1].u.memref.mobj = *mobj; 123 124 res = thread_rpc_cmd(OPTEE_RPC_CMD_LOAD_TA, 2, params); 125 exit: 126 if (res != TEE_SUCCESS) 127 thread_rpc_free_payload(*mobj); 128 129 return res; 130 } 131 132 static TEE_Result ree_fs_ta_open(const TEE_UUID *uuid, 133 struct ts_store_handle **h) 134 { 135 struct ree_fs_ta_handle *handle; 136 struct shdr *shdr = NULL; 137 struct mobj *mobj = NULL; 138 void *hash_ctx = NULL; 139 struct shdr *ta = NULL; 140 size_t ta_size = 0; 141 TEE_Result res = TEE_SUCCESS; 142 size_t offs = 0; 143 struct shdr_bootstrap_ta *bs_hdr = NULL; 144 struct shdr_encrypted_ta *ehdr = NULL; 145 size_t shdr_sz = 0; 146 147 handle = calloc(1, sizeof(*handle)); 148 if (!handle) 149 return TEE_ERROR_OUT_OF_MEMORY; 150 151 /* Request TA from tee-supplicant */ 152 res = rpc_load(uuid, &ta, &ta_size, &mobj); 153 if (res != TEE_SUCCESS) 154 goto error; 155 156 /* Make secure copy of signed header */ 157 shdr = shdr_alloc_and_copy(0, ta, ta_size); 158 if (!shdr) { 159 res = TEE_ERROR_SECURITY; 160 goto error_free_payload; 161 } 162 163 /* Validate header signature */ 164 res = shdr_verify_signature(shdr); 165 if (res != TEE_SUCCESS) 166 goto error_free_payload; 167 if (shdr->img_type != SHDR_TA && shdr->img_type != SHDR_BOOTSTRAP_TA && 168 shdr->img_type != SHDR_ENCRYPTED_TA) { 169 res = TEE_ERROR_SECURITY; 170 goto error_free_payload; 171 } 172 173 /* 174 * Initialize a hash context and run the algorithm over the signed 175 * header (less the final file hash and its signature of course) 176 */ 177 res = crypto_hash_alloc_ctx(&hash_ctx, 178 TEE_DIGEST_HASH_TO_ALGO(shdr->algo)); 179 if (res != TEE_SUCCESS) 180 goto error_free_payload; 181 res = crypto_hash_init(hash_ctx); 182 if (res != TEE_SUCCESS) 183 goto error_free_hash; 184 res = crypto_hash_update(hash_ctx, (uint8_t *)shdr, sizeof(*shdr)); 185 if (res != TEE_SUCCESS) 186 goto error_free_hash; 187 shdr_sz = SHDR_GET_SIZE(shdr); 188 if (!shdr_sz) { 189 res = TEE_ERROR_SECURITY; 190 goto error_free_hash; 191 } 192 offs = shdr_sz; 193 194 if (shdr->img_type == SHDR_BOOTSTRAP_TA || 195 shdr->img_type == SHDR_ENCRYPTED_TA) { 196 TEE_UUID bs_uuid = { }; 197 size_t sz = shdr_sz; 198 199 if (ADD_OVERFLOW(sz, sizeof(*bs_hdr), &sz) || ta_size < sz) { 200 res = TEE_ERROR_SECURITY; 201 goto error_free_hash; 202 } 203 204 bs_hdr = malloc(sizeof(*bs_hdr)); 205 if (!bs_hdr) { 206 res = TEE_ERROR_OUT_OF_MEMORY; 207 goto error_free_hash; 208 } 209 210 memcpy(bs_hdr, (uint8_t *)ta + offs, sizeof(*bs_hdr)); 211 212 /* 213 * There's a check later that the UUID embedded inside the 214 * ELF is matching, but since we now have easy access to 215 * the expected uuid of the TA we check it a bit earlier 216 * here. 217 */ 218 tee_uuid_from_octets(&bs_uuid, bs_hdr->uuid); 219 if (memcmp(&bs_uuid, uuid, sizeof(TEE_UUID))) { 220 res = TEE_ERROR_SECURITY; 221 goto error_free_hash; 222 } 223 224 res = crypto_hash_update(hash_ctx, (uint8_t *)bs_hdr, 225 sizeof(*bs_hdr)); 226 if (res != TEE_SUCCESS) 227 goto error_free_hash; 228 offs += sizeof(*bs_hdr); 229 handle->bs_hdr = bs_hdr; 230 } 231 232 if (shdr->img_type == SHDR_ENCRYPTED_TA) { 233 struct shdr_encrypted_ta img_ehdr = { }; 234 size_t sz = shdr_sz; 235 size_t ehdr_sz = 0; 236 237 if (ADD_OVERFLOW(sz, sizeof(struct shdr_bootstrap_ta), &sz) || 238 ADD_OVERFLOW(sz, sizeof(img_ehdr), &sz) || 239 ta_size < sz) { 240 res = TEE_ERROR_SECURITY; 241 goto error_free_hash; 242 } 243 244 memcpy(&img_ehdr, ((uint8_t *)ta + offs), sizeof(img_ehdr)); 245 ehdr_sz = SHDR_ENC_GET_SIZE(&img_ehdr); 246 sz -= sizeof(img_ehdr); 247 if (!ehdr_sz || ADD_OVERFLOW(sz, ehdr_sz, &sz) || 248 ta_size < sz) { 249 res = TEE_ERROR_SECURITY; 250 goto error_free_hash; 251 } 252 253 254 ehdr = malloc(ehdr_sz); 255 if (!ehdr) { 256 res = TEE_ERROR_OUT_OF_MEMORY; 257 goto error_free_hash; 258 } 259 260 memcpy(ehdr, ((uint8_t *)ta + offs), ehdr_sz); 261 262 res = crypto_hash_update(hash_ctx, (uint8_t *)ehdr, ehdr_sz); 263 if (res != TEE_SUCCESS) 264 goto error_free_hash; 265 266 res = tee_ta_decrypt_init(&handle->enc_ctx, ehdr, 267 shdr->img_size); 268 if (res != TEE_SUCCESS) 269 goto error_free_hash; 270 271 offs += ehdr_sz; 272 handle->ehdr = ehdr; 273 } 274 275 if (ta_size != offs + shdr->img_size) { 276 res = TEE_ERROR_SECURITY; 277 goto error_free_hash; 278 } 279 280 handle->nw_ta = ta; 281 handle->nw_ta_size = ta_size; 282 handle->offs = offs; 283 handle->hash_ctx = hash_ctx; 284 handle->shdr = shdr; 285 handle->mobj = mobj; 286 *h = (struct ts_store_handle *)handle; 287 return TEE_SUCCESS; 288 289 error_free_hash: 290 crypto_hash_free_ctx(hash_ctx); 291 error_free_payload: 292 thread_rpc_free_payload(mobj); 293 error: 294 free(ehdr); 295 free(bs_hdr); 296 shdr_free(shdr); 297 free(handle); 298 return res; 299 } 300 301 static TEE_Result ree_fs_ta_get_size(const struct ts_store_handle *h, 302 size_t *size) 303 { 304 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 305 306 *size = handle->shdr->img_size; 307 return TEE_SUCCESS; 308 } 309 310 static TEE_Result ree_fs_ta_get_tag(const struct ts_store_handle *h, 311 uint8_t *tag, unsigned int *tag_len) 312 { 313 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 314 315 if (!tag || *tag_len < handle->shdr->hash_size) { 316 *tag_len = handle->shdr->hash_size; 317 return TEE_ERROR_SHORT_BUFFER; 318 } 319 *tag_len = handle->shdr->hash_size; 320 321 memcpy(tag, SHDR_GET_HASH(handle->shdr), handle->shdr->hash_size); 322 323 return TEE_SUCCESS; 324 } 325 326 static TEE_Result check_digest(struct ree_fs_ta_handle *h) 327 { 328 void *digest = NULL; 329 TEE_Result res; 330 331 digest = malloc(h->shdr->hash_size); 332 if (!digest) 333 return TEE_ERROR_OUT_OF_MEMORY; 334 res = crypto_hash_final(h->hash_ctx, digest, h->shdr->hash_size); 335 if (res != TEE_SUCCESS) { 336 res = TEE_ERROR_SECURITY; 337 goto out; 338 } 339 if (memcmp(digest, SHDR_GET_HASH(h->shdr), h->shdr->hash_size)) 340 res = TEE_ERROR_SECURITY; 341 out: 342 free(digest); 343 return res; 344 } 345 346 static TEE_Result check_update_version(const char *db_name, 347 const uint8_t uuid[sizeof(TEE_UUID)], 348 uint32_t version) 349 { 350 struct ver_db_entry db_entry = { }; 351 const struct tee_file_operations *ops = NULL; 352 struct tee_file_handle *fh = NULL; 353 TEE_Result res = TEE_SUCCESS; 354 bool entry_found = false; 355 size_t len = 0; 356 unsigned int i = 0; 357 struct ver_db_hdr db_hdr = { }; 358 struct tee_pobj pobj = { 359 .obj_id = (void *)db_name, 360 .obj_id_len = strlen(db_name) + 1, 361 }; 362 363 ops = tee_svc_storage_file_ops(TEE_STORAGE_PRIVATE); 364 if (!ops) 365 return TEE_SUCCESS; /* Compiled with no secure storage */ 366 367 mutex_lock(&ver_db_mutex); 368 369 res = ops->open(&pobj, NULL, &fh); 370 if (res != TEE_SUCCESS && res != TEE_ERROR_ITEM_NOT_FOUND) 371 goto out; 372 373 if (res == TEE_ERROR_ITEM_NOT_FOUND) { 374 res = ops->create(&pobj, false, NULL, 0, NULL, 0, NULL, 0, &fh); 375 if (res != TEE_SUCCESS) 376 goto out; 377 378 res = ops->write(fh, 0, &db_hdr, sizeof(db_hdr)); 379 if (res != TEE_SUCCESS) 380 goto out; 381 } else { 382 len = sizeof(db_hdr); 383 384 res = ops->read(fh, 0, &db_hdr, &len); 385 if (res != TEE_SUCCESS) { 386 goto out; 387 } else if (len != sizeof(db_hdr)) { 388 res = TEE_ERROR_BAD_STATE; 389 goto out; 390 } 391 } 392 393 for (i = 0; i < db_hdr.nb_entries; i++) { 394 len = sizeof(db_entry); 395 396 res = ops->read(fh, sizeof(db_hdr) + (i * len), &db_entry, 397 &len); 398 if (res != TEE_SUCCESS) { 399 goto out; 400 } else if (len != sizeof(db_entry)) { 401 res = TEE_ERROR_BAD_STATE; 402 goto out; 403 } 404 405 if (!memcmp(uuid, db_entry.uuid, sizeof(TEE_UUID))) { 406 entry_found = true; 407 break; 408 } 409 } 410 411 if (entry_found) { 412 if (db_entry.version > version) { 413 res = TEE_ERROR_ACCESS_CONFLICT; 414 goto out; 415 } else if (db_entry.version < version) { 416 memcpy(db_entry.uuid, uuid, sizeof(TEE_UUID)); 417 db_entry.version = version; 418 len = sizeof(db_entry); 419 res = ops->write(fh, sizeof(db_hdr) + (i * len), 420 &db_entry, len); 421 if (res != TEE_SUCCESS) 422 goto out; 423 } 424 } else { 425 memcpy(db_entry.uuid, uuid, sizeof(TEE_UUID)); 426 db_entry.version = version; 427 len = sizeof(db_entry); 428 res = ops->write(fh, sizeof(db_hdr) + (db_hdr.nb_entries * len), 429 &db_entry, len); 430 if (res != TEE_SUCCESS) 431 goto out; 432 433 db_hdr.nb_entries++; 434 res = ops->write(fh, 0, &db_hdr, sizeof(db_hdr)); 435 if (res != TEE_SUCCESS) 436 goto out; 437 } 438 439 out: 440 ops->close(&fh); 441 mutex_unlock(&ver_db_mutex); 442 return res; 443 } 444 445 static TEE_Result ree_fs_ta_read(struct ts_store_handle *h, void *data, 446 size_t len) 447 { 448 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 449 450 uint8_t *src = (uint8_t *)handle->nw_ta + handle->offs; 451 size_t next_offs = 0; 452 uint8_t *dst = src; 453 TEE_Result res = TEE_SUCCESS; 454 455 if (ADD_OVERFLOW(handle->offs, len, &next_offs) || 456 next_offs > handle->nw_ta_size) 457 return TEE_ERROR_BAD_PARAMETERS; 458 459 if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) { 460 if (data) { 461 dst = data; /* Hash secure buffer */ 462 res = tee_ta_decrypt_update(handle->enc_ctx, dst, src, 463 len); 464 if (res != TEE_SUCCESS) 465 return TEE_ERROR_SECURITY; 466 } else { 467 size_t num_bytes = 0; 468 size_t b_size = MIN(1024U, len); 469 uint8_t *b = malloc(b_size); 470 471 if (!b) 472 return TEE_ERROR_OUT_OF_MEMORY; 473 474 dst = NULL; 475 while (num_bytes < len) { 476 size_t n = MIN(b_size, len - num_bytes); 477 478 res = tee_ta_decrypt_update(handle->enc_ctx, b, 479 src + num_bytes, n); 480 if (res) 481 break; 482 num_bytes += n; 483 484 res = crypto_hash_update(handle->hash_ctx, b, 485 n); 486 if (res) 487 break; 488 } 489 490 free(b); 491 if (res != TEE_SUCCESS) 492 return TEE_ERROR_SECURITY; 493 } 494 } else if (data) { 495 dst = data; /* Hash secure buffer (shm might be modified) */ 496 memcpy(dst, src, len); 497 } 498 499 if (dst) { 500 res = crypto_hash_update(handle->hash_ctx, dst, len); 501 if (res != TEE_SUCCESS) 502 return TEE_ERROR_SECURITY; 503 } 504 505 handle->offs = next_offs; 506 if (handle->offs == handle->nw_ta_size) { 507 if (handle->shdr->img_type == SHDR_ENCRYPTED_TA) { 508 /* 509 * Last read: time to finalize authenticated 510 * decryption. 511 */ 512 res = tee_ta_decrypt_final(handle->enc_ctx, 513 handle->ehdr, NULL, NULL, 0); 514 if (res != TEE_SUCCESS) 515 return TEE_ERROR_SECURITY; 516 } 517 /* 518 * Last read: time to check if our digest matches the expected 519 * one (from the signed header) 520 */ 521 res = check_digest(handle); 522 if (res != TEE_SUCCESS) 523 return res; 524 525 if (handle->bs_hdr) 526 res = check_update_version(ta_ver_db, 527 handle->bs_hdr->uuid, 528 handle->bs_hdr->ta_version); 529 } 530 return res; 531 } 532 533 static void ree_fs_ta_close(struct ts_store_handle *h) 534 { 535 struct ree_fs_ta_handle *handle = (struct ree_fs_ta_handle *)h; 536 537 if (!handle) 538 return; 539 thread_rpc_free_payload(handle->mobj); 540 crypto_hash_free_ctx(handle->hash_ctx); 541 free(handle->shdr); 542 free(handle->ehdr); 543 free(handle->bs_hdr); 544 free(handle); 545 } 546 547 #ifndef CFG_REE_FS_TA_BUFFERED 548 REGISTER_TA_STORE(9) = { 549 .description = "REE", 550 .open = ree_fs_ta_open, 551 .get_size = ree_fs_ta_get_size, 552 .get_tag = ree_fs_ta_get_tag, 553 .read = ree_fs_ta_read, 554 .close = ree_fs_ta_close, 555 }; 556 #endif 557 558 #ifdef CFG_REE_FS_TA_BUFFERED 559 560 /* 561 * This is a wrapper around the "REE FS" TA store. 562 * The whole TA/library is read into a temporary buffer during .open(). This 563 * allows the binary to be authenticated before any data is read and processed 564 * by the upper layer (ELF loader). 565 */ 566 567 struct buf_ree_fs_ta_handle { 568 struct ts_store_handle *h; /* Note: a REE FS TA store handle */ 569 size_t ta_size; 570 tee_mm_entry_t *mm; 571 uint8_t *buf; 572 size_t offs; 573 uint8_t *tag; 574 unsigned int tag_len; 575 }; 576 577 static TEE_Result buf_ta_open(const TEE_UUID *uuid, 578 struct ts_store_handle **h) 579 { 580 struct buf_ree_fs_ta_handle *handle = NULL; 581 TEE_Result res = TEE_SUCCESS; 582 583 handle = calloc(1, sizeof(*handle)); 584 if (!handle) 585 return TEE_ERROR_OUT_OF_MEMORY; 586 res = ree_fs_ta_open(uuid, &handle->h); 587 if (res) 588 goto err2; 589 res = ree_fs_ta_get_size(handle->h, &handle->ta_size); 590 if (res) 591 goto err; 592 593 res = ree_fs_ta_get_tag(handle->h, NULL, &handle->tag_len); 594 if (res != TEE_ERROR_SHORT_BUFFER) { 595 res = TEE_ERROR_GENERIC; 596 goto err; 597 } 598 handle->tag = malloc(handle->tag_len); 599 if (!handle->tag) { 600 res = TEE_ERROR_OUT_OF_MEMORY; 601 goto err; 602 } 603 res = ree_fs_ta_get_tag(handle->h, handle->tag, &handle->tag_len); 604 if (res) 605 goto err; 606 607 handle->mm = tee_mm_alloc(&tee_mm_sec_ddr, handle->ta_size); 608 if (!handle->mm) { 609 res = TEE_ERROR_OUT_OF_MEMORY; 610 goto err; 611 } 612 handle->buf = phys_to_virt(tee_mm_get_smem(handle->mm), 613 MEM_AREA_TA_RAM, handle->ta_size); 614 if (!handle->buf) { 615 res = TEE_ERROR_OUT_OF_MEMORY; 616 goto err; 617 } 618 res = ree_fs_ta_read(handle->h, handle->buf, handle->ta_size); 619 if (res) 620 goto err; 621 *h = (struct ts_store_handle *)handle; 622 err: 623 ree_fs_ta_close(handle->h); 624 err2: 625 if (res) { 626 tee_mm_free(handle->mm); 627 free(handle->tag); 628 free(handle); 629 } 630 return res; 631 } 632 633 static TEE_Result buf_ta_get_size(const struct ts_store_handle *h, 634 size_t *size) 635 { 636 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 637 638 *size = handle->ta_size; 639 return TEE_SUCCESS; 640 } 641 642 static TEE_Result buf_ta_read(struct ts_store_handle *h, void *data, 643 size_t len) 644 { 645 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 646 uint8_t *src = handle->buf + handle->offs; 647 size_t next_offs = 0; 648 649 if (ADD_OVERFLOW(handle->offs, len, &next_offs) || 650 next_offs > handle->ta_size) 651 return TEE_ERROR_BAD_PARAMETERS; 652 653 if (data) 654 memcpy(data, src, len); 655 handle->offs = next_offs; 656 return TEE_SUCCESS; 657 } 658 659 static TEE_Result buf_ta_get_tag(const struct ts_store_handle *h, 660 uint8_t *tag, unsigned int *tag_len) 661 { 662 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 663 664 *tag_len = handle->tag_len; 665 if (!tag || *tag_len < handle->tag_len) 666 return TEE_ERROR_SHORT_BUFFER; 667 668 memcpy(tag, handle->tag, handle->tag_len); 669 670 return TEE_SUCCESS; 671 } 672 673 static void buf_ta_close(struct ts_store_handle *h) 674 { 675 struct buf_ree_fs_ta_handle *handle = (struct buf_ree_fs_ta_handle *)h; 676 677 if (!handle) 678 return; 679 tee_mm_free(handle->mm); 680 free(handle->tag); 681 free(handle); 682 } 683 684 REGISTER_TA_STORE(9) = { 685 .description = "REE [buffered]", 686 .open = buf_ta_open, 687 .get_size = buf_ta_get_size, 688 .get_tag = buf_ta_get_tag, 689 .read = buf_ta_read, 690 .close = buf_ta_close, 691 }; 692 693 #endif /* CFG_REE_FS_TA_BUFFERED */ 694