QUESTION
session은 context를 찾기 전부터 존재하는가?
tee_ta_init_session()은 calloc()으로 세션을 만들고 condvar·초기 refcount·ID를 설정한 뒤 TAILQ_INSERT_TAIL(open_sessions, s, link)로 client 목록에 연결한다. 그 다음에 기존 TA context, secure partition, pseudo TA, user TA 순서로 찾는다.
목록 연결은 곧바로 성공 ID를 caller에게 내보내는 단계가 아니다. context 연결 또는 TA open entry가 실패하면 세션을 목록에서 제거·해제하며, tee_ta_open_session()은 성공 끝에 *sess_id를 쓴다.
정상 context는 TA enter_close_session()을 먼저 실행하고 destroy_session()으로 들어간다. 그 안에서 unlink 표시, 남은 참조 대기, 목록 제거, 세션 해제가 진행된다.
STRUCTURE
session 목록과 TA context는 다른 수명
open_sessions와 세션 IDtee_ta_session·refcountts_sess.ctx·TA flagsunlink·ref 대기·freeclient 목록에는 context 탐색 전에 삽입한다. 외부 session ID는 성공한 open이 끝날 때 반환한다.
고정 tee_ta_manager.c의 소유권 관계다. tee_ta_mutex 아래 연결하므로 이 삽입을 동시 caller에게 즉시 보이는 성공 세션으로 읽지 않는다.
CALL PATH
초기화, context 연결, open entry, close
tee_ta_open_session()의 실제 순서calloc()·초기 ref새 ID를 만들 수 없으면 삽입 전 세션만 해제한다. 삽입 뒤 초기화 실패는 목록에서 제거하고 해제한다. 초기화 뒤 parameter·TA open entry 실패는 close_session()으로 정리한다.
STATE LEDGER
세션 생성과 context 연결 시점
목록 연결, context 초기화, 성공 ID 반환을 각각 구분한다.
| 순서 | 고정 코드 | 세션 상태 | context 상태 | 실패 시 정리 |
|---|---|---|---|---|
| 01 | calloc()·condvar·ref_count = 1 | 미연결 세션 객체 | 아직 탐색 전 | 할당 실패 즉시 반환 |
| 02 | new_session_id()·TAILQ_INSERT_TAIL() | client 목록 연결·현재 thread가 잠금 보유 | 아직 탐색 전 | ID 실패는 삽입 없이 free |
| 03 | 기존 context → secure partition → pseudo TA → user TA | 같은 세션 객체에 ts_sess.ctx 연결 시도 | 재사용 또는 새 context 초기화 | 실패하면 목록 제거·free |
| 04 | check_params()·enter_open_session() | 성공 전까지 외부 ID 미반환 | busy/panic 정책 적용 | close_session() |
| 05 | *sess_id = s->id·tee_ta_put_session() | 성공 ID 반환; open이 잡은 초기 ref 해제 | 세션이 context 참조 유지 | close까지 client 목록에 남음 |
| 06 | 정상 close entry → destroy/unlink | unlink=true, ref_count == 1 대기, 목록 제거 | 세션 정리 후 context ref 감소·조건부 destroy | panic/ctx 없음은 close entry 생략 |
close가 기다리는 ref_count == 1은 close caller가 가진 참조 하나가 남은 상태다. “0까지 기다린 뒤 CloseSession”이 아니다.
VISUAL WALKTHROUGH
목록 삽입과 close entry의 앞뒤
그림 3은 세션 초기화, 그림 4는 정상 context의 close 경로다. 실패·panic 분기는 그림 아래에 분리했다.
tee_ta_init_session()의 생성·연결·탐색 순서.
calloc()open_sessions 삽입close_session() → destroy_session() → tee_ta_unlink_session().
enter_close_session()destroy_session()unlink=true·ref 대기UPSTREAM SOURCE
원본 코드
아래 코드는 OP-TEE OS 4.10.0의 core/kernel/tee_ta_manager.c에서 682-770줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.
682 keep_alive = ctx->flags & TA_FLAG_INSTANCE_KEEP_ALIVE;
683 if (keep_alive)
684 keep_crashed = ctx->flags & TA_FLAG_INSTANCE_KEEP_CRASHED;
685
686 /*
687 * Keep panicked TAs with SINGLE_INSTANCE, KEEP_ALIVE, and KEEP_CRASHED
688 * flags in the context list to maintain their panicked status and
689 * prevent respawning.
690 */
691 if (!keep_crashed) {
692 mutex_lock(&tee_ta_mutex);
693 was_releasing = ctx->is_releasing;
694 ctx->is_releasing = true;
695 if (!was_releasing) {
696 DMSG("Releasing panicked TA ctx");
697 TAILQ_REMOVE(&tee_ctxes, ctx, link);
698 }
699 mutex_unlock(&tee_ta_mutex);
700
701 if (!was_releasing)
702 ctx->ts_ctx.ops->release_state(&ctx->ts_ctx);
703 }
704}
705
706TEE_Result tee_ta_open_session(TEE_ErrorOrigin *err, uint32_t *sess_id,
707 struct tee_ta_session_head *open_sessions,
708 const TEE_UUID *uuid,
709 const TEE_Identity *clnt_id,
710 uint32_t cancel_req_to,
711 struct tee_ta_param *param)
712{
713 TEE_Result res = TEE_SUCCESS;
714 struct tee_ta_session *s = NULL;
715 struct tee_ta_ctx *ctx = NULL;
716 struct ts_ctx *ts_ctx = NULL;
717 bool panicked = false;
718 bool was_busy = false;
719
720 res = tee_ta_init_session(err, open_sessions, uuid, &s);
721 if (res != TEE_SUCCESS) {
722 DMSG("init session failed 0x%x", res);
723 return res;
724 }
725
726 if (!check_params(s, param)) {
727 close_session(s, open_sessions);
728 return TEE_ERROR_BAD_PARAMETERS;
729 }
730
731 ts_ctx = s->ts_sess.ctx;
732 ctx = ts_to_ta_ctx(ts_ctx);
733
734 if (tee_ta_try_set_busy(ctx)) {
735 if (!ctx->panicked) {
736 /* Save identity of the owner of the session */
737 s->clnt_id = *clnt_id;
738 s->param = param;
739 set_invoke_timeout(s, cancel_req_to);
740 res = ts_ctx->ops->enter_open_session(&s->ts_sess);
741 s->param = NULL;
742 }
743
744 panicked = ctx->panicked;
745 if (panicked) {
746 maybe_release_ta_ctx(ctx);
747 res = TEE_ERROR_TARGET_DEAD;
748 } else {
749 if (IS_ENABLED(CFG_FTRACE_DUMP_EVERY_ENTRY))
750 dump_ftrace(s);
751 }
752
753 tee_ta_clear_busy(ctx);
754 } else {
755 /* Deadlock avoided */
756 res = TEE_ERROR_BUSY;
757 was_busy = true;
758 }
759
760 /*
761 * Origin error equal to TEE_ORIGIN_TRUSTED_APP for "regular" error,
762 * apart from panicking.
763 */
764 if (panicked || was_busy)
765 *err = TEE_ORIGIN_TEE;
766 else
767 *err = s->err_origin;
768
769 if (res) {
770 close_session(s, open_sessions);
READING CONTRACT
발췌 앞뒤의 호출 순서
원본 발췌는 tee_ta_open_session() 주변을 보여 준다. 세션이 먼저 할당·삽입되는 근거는 tee_ta_init_session()이다. 종료 순서는 close_session()과 tee_ta_unlink_session()·destroy_session()을 함께 대조한다.
발췌 안의 *sess_id 기록은 외부 성공 결과이며, 더 이른 TAILQ_INSERT_TAIL()과 구분한다.
RELATED SOURCE
함께 읽어야 하는 원본 코드
첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.
01. 열린 session에서 TA command를 호출
core/kernel/tee_ta_manager.c 768-858줄이다.
768
769 if (res) {
770 close_session(s, open_sessions);
771 EMSG("Failed for TA %pUl. Return error %#"PRIx32, uuid, res);
772 } else {
773 *sess_id = s->id;
774 tee_ta_put_session(s);
775 }
776
777 return res;
778}
779
780TEE_Result tee_ta_invoke_command(TEE_ErrorOrigin *err,
781 struct tee_ta_session *sess,
782 const TEE_Identity *clnt_id,
783 uint32_t cancel_req_to, uint32_t cmd,
784 struct tee_ta_param *param)
785{
786 struct tee_ta_ctx *ta_ctx = NULL;
787 struct ts_ctx *ts_ctx = NULL;
788 TEE_Result res = TEE_SUCCESS;
789 bool panicked = false;
790
791 if (check_client(sess, clnt_id) != TEE_SUCCESS)
792 return TEE_ERROR_BAD_PARAMETERS; /* intentional generic error */
793
794 if (!check_params(sess, param))
795 return TEE_ERROR_BAD_PARAMETERS;
796
797 ts_ctx = sess->ts_sess.ctx;
798 ta_ctx = ts_to_ta_ctx(ts_ctx);
799
800 tee_ta_set_busy(ta_ctx);
801
802 if (!ta_ctx->panicked) {
803 sess->param = param;
804 set_invoke_timeout(sess, cancel_req_to);
805 res = ts_ctx->ops->enter_invoke_cmd(&sess->ts_sess, cmd);
806 sess->param = NULL;
807 }
808
809 panicked = ta_ctx->panicked;
810 if (panicked) {
811 maybe_release_ta_ctx(ta_ctx);
812 res = TEE_ERROR_TARGET_DEAD;
813 } else {
814 if (IS_ENABLED(CFG_FTRACE_DUMP_EVERY_ENTRY))
815 dump_ftrace(sess);
816 }
817
818 tee_ta_clear_busy(ta_ctx);
819
820 /*
821 * Origin error equal to TEE_ORIGIN_TRUSTED_APP for "regular" error,
822 * apart from panicking.
823 */
824 if (panicked)
825 *err = TEE_ORIGIN_TEE;
826 else
827 *err = sess->err_origin;
828
829 /* Short buffer is not an effective error case */
830 if (res != TEE_SUCCESS && res != TEE_ERROR_SHORT_BUFFER)
831 DMSG("Error: %x of %d", res, *err);
832
833 return res;
834}
835
836#if defined(CFG_TA_STATS)
837static TEE_Result dump_ta_memstats(struct tee_ta_session *s,
838 struct tee_ta_param *param)
839{
840 TEE_Result res = TEE_SUCCESS;
841 struct tee_ta_ctx *ctx = NULL;
842 struct ts_ctx *ts_ctx = NULL;
843 bool panicked = false;
844
845 ts_ctx = s->ts_sess.ctx;
846 if (!ts_ctx)
847 return TEE_ERROR_ITEM_NOT_FOUND;
848
849 ctx = ts_to_ta_ctx(ts_ctx);
850
851 if (ctx->is_initializing)
852 return TEE_ERROR_BAD_STATE;
853
854 if (tee_ta_try_set_busy(ctx)) {
855 if (!ctx->panicked) {
856 s->param = param;
857 set_invoke_timeout(s, TEE_TIMEOUT_INFINITE);
858 res = ts_ctx->ops->dump_mem_stats(&s->ts_sess);
02. TA close entry를 실행한 뒤 세션을 정리하는 함수
core/kernel/tee_ta_manager.c 495-585줄이다.
495 ctx->is_releasing = true;
496 }
497 mutex_unlock(&tee_ta_mutex);
498
499 destroy_context(ctx);
500 } else
501 mutex_unlock(&tee_ta_mutex);
502}
503
504/*
505 * Close a Trusted Application and free available resources
506 */
507TEE_Result tee_ta_close_session(uint32_t id,
508 struct tee_ta_session_head *open_sessions,
509 const TEE_Identity *clnt_id)
510{
511 struct tee_ta_session *sess = NULL;
512
513 DMSG("id %"PRIu32, id);
514
515 sess = tee_ta_get_session(id, true, open_sessions);
516
517 if (!sess) {
518 EMSG("session id %"PRIu32" to be removed is not found", id);
519 return TEE_ERROR_ITEM_NOT_FOUND;
520 }
521
522 if (check_client(sess, clnt_id) != TEE_SUCCESS) {
523 tee_ta_put_session(sess);
524 return TEE_ERROR_BAD_PARAMETERS; /* intentional generic error */
525 }
526
527 DMSG("Destroy session");
528 close_session(sess, open_sessions);
529
530 return TEE_SUCCESS;
531}
532
533static TEE_Result tee_ta_init_session_with_context(struct tee_ta_session *s,
534 const TEE_UUID *uuid)
535{
536 struct tee_ta_ctx *ctx = NULL;
537
538 while (true) {
539 ctx = tee_ta_context_find(uuid);
540 if (!ctx)
541 return TEE_ERROR_ITEM_NOT_FOUND;
542
543 if (!ctx->is_initializing)
544 break;
545 /*
546 * Context is still initializing, wait here until it's
547 * fully initialized. Note that we're searching for the
548 * context again since it may have been removed while we
549 * where sleeping.
550 */
551 condvar_wait(&tee_ta_init_cv, &tee_ta_mutex);
552 }
553
554 /*
555 * If the trusted service is not a single instance service (e.g. is
556 * a multi-instance TA) it should be loaded as a new instance instead
557 * of doing anything with this instance. So tell the caller that we
558 * didn't find the TA it the caller will load a new instance.
559 */
560 if ((ctx->flags & TA_FLAG_SINGLE_INSTANCE) == 0)
561 return TEE_ERROR_ITEM_NOT_FOUND;
562
563 /*
564 * The trusted service is single instance, if it isn't multi session we
565 * can't create another session unless its reference is zero
566 */
567 if (!(ctx->flags & TA_FLAG_MULTI_SESSION) && ctx->ref_count)
568 return TEE_ERROR_BUSY;
569
570 DMSG("Re-open trusted service %pUl", (void *)&ctx->ts_ctx.uuid);
571
572 ctx->ref_count++;
573 s->ts_sess.ctx = &ctx->ts_ctx;
574 s->ts_sess.handle_scall = s->ts_sess.ctx->ops->handle_scall;
575 return TEE_SUCCESS;
576}
577
578static uint32_t new_session_id(struct tee_ta_session_head *open_sessions)
579{
580 struct tee_ta_session *last = NULL;
581 uint32_t saved = 0;
582 uint32_t id = 1;
583
584 last = TAILQ_LAST(open_sessions, tee_ta_session_head);
585 if (last) {
DETAILS
고정 manager의 open과 close
세션을 먼저 만든다
tee_ta_init_session()은 calloc() 뒤 cancellation flag, condvar, lock_thread, ref_count = 1을 초기화한다. tee_ta_mutex 아래 새 ID를 고른 뒤 open_sessions에 삽입한다.
context 후보를 세션에 연결한다
목록 삽입 다음에 기존 context를 시도하고, 없으면 secure partition, pseudo TA, user TA 순서로 탐색·초기화한다. 다음 후보는 TEE_ERROR_ITEM_NOT_FOUND일 때만 시도한다. 기존 single-instance context가 multi-session을 허용하지 않고 참조가 남아 있으면 TEE_ERROR_BUSY를 반환한다.
초기화 실패는 목록에서 되돌린다
ID 생성 실패는 아직 삽입되지 않은 세션을 바로 해제한다. 그 뒤 context 경로 실패는 TAILQ_REMOVE(open_sessions, s, link)와 free(s)로 정리한다. 부분 context 정리는 해당 loader의 경로도 확인한다.
TA open entry 성공 뒤 ID를 돌려준다
tee_ta_open_session()은 parameter를 검사하고 context busy 상태에서 enter_open_session()을 호출한다. 실패 시 close_session()을 거치고, 성공 시 *sess_id = s->id를 기록한 뒤 tee_ta_put_session()으로 open caller의 초기 ref를 해제한다.
정상 close entry가 먼저다
tee_ta_close_session()은 exclusive session ref와 client 검사를 거친다. 정상 context라면 tee_ta_set_busy(), enter_close_session(), destroy_session(), tee_ta_clear_busy() 순서다. 다만 TA_FLAG_CONCURRENT가 있으면 busy 설정·해제 함수는 그 상태를 변경하지 않고 반환한다. context가 없거나 panicked이면 close entry 없이 destroy로 간다.
IMPLEMENTATION NOTES
unlink가 차단하는 새 접근과 마지막 context
목록 연결과 성공 공개는 다르다
삽입은 tee_ta_mutex를 잡은 초기화 함수 안에서 context 탐색 전에 일어난다. 그러나 caller에게 돌려주는 성공 ID는 TA open entry가 성공한 뒤에야 기록된다. 실패한 임시 세션을 성공한 client handle로 세지 않는다.
unlink는 다른 참조가 빠진 뒤 제거한다
tee_ta_unlink_session()은 unlink=true와 condvar broadcast로 새 get을 막고, ref_count == 1을 기다린 뒤 목록에서 제거한다. 남은 하나는 close caller가 가진 참조다. 이후 세션 객체를 해제한다.
context는 별도 참조·flag로 정리한다
세션 destroy 뒤 ctx->ref_count를 감소시킨다. 0이면 panicked, single-instance keep-alive, keep-crashed flag 조합에 따라 context 목록 제거와 destroy_context() 여부를 결정한다. 세션 제거와 context 해제를 같은 사건으로 묶지 않는다.
DEEP ANALYSIS
레지스터에서 오류 판정까지 상세 분석
TA context와 session은 같은 객체가 아니다. tee_ta_ctx는 TA binary, address space와 global instance state를 나타내고 여러 session이 공유할 수 있으며, tee_ta_session은 한 client의 open 관계, cancellation과 invoke state를 보유한다.
single-instance, multi-session, keep-alive flag에 따라 context 재사용과 destruction 시점이 달라진다. session list lock, context busy state, ref_count와 condvar가 어떤 순서로 사용되는지 보지 않으면 close와 concurrent invoke 사이 use-after-free를 놓치게 된다.
load 가능한 signed TA 또는 등록된 pseudo TA를 정확히 지칭해야 한다.
login method와 client UUID가 policy대로 정규화돼야 한다.
해당 client가 소유한 session namespace여야 한다.
single-instance, multi-session, keep-alive 조합이 유효해야 한다.
open/invoke 동안 secure object와 mobj ref를 유지해야 한다.
이 다섯 항목은 함수 호출 순서가 아니라 서로 다른 입력 조건이다. session 목록, client identity, TA flag, parameter의 유효성을 각각 확인한다.
진입 레지스터와 메모리 계약
함수 첫 줄에 도달했을 때 이미 참이라고 가정하는 값과, 그 값이 틀렸을 때 영향을 받는 범위를 함께 적었다.
| # | 입력 상태 | 생산자 | 정상 조건 | 확인 이유 |
|---|---|---|---|---|
| 01 | TA UUID | open-session meta parameter | load 가능한 signed TA 또는 등록된 pseudo TA를 정확히 지칭해야 한다. | context lookup/creation key다. |
| 02 | client identity | Normal World/session metadata | login method와 client UUID가 policy대로 정규화돼야 한다. | TA access control과 session identity를 결정한다. |
| 03 | open_sessions list | caller/client context | 해당 client가 소유한 session namespace여야 한다. | session ID lookup과 close 범위를 한정한다. |
| 04 | TA flags | signed header/pseudo TA descriptor | single-instance, multi-session, keep-alive 조합이 유효해야 한다. | context 공유와 lifetime policy를 정한다. |
| 05 | tee_ta_param | message conversion | open/invoke 동안 secure object와 mobj ref를 유지해야 한다. | TA entry parameter와 cleanup의 입력이다. |
핵심 구조체 필드의 생산자, 소비자와 수명
구조체 이름만 나열하지 않고 어떤 코드가 값을 쓰고, 어느 코드가 처음 읽으며, 언제까지 주소와 내용이 유지되어야 하는지 구분했다.
| # | 객체 또는 필드 | 생산자 | 소비자 | 수명과 불변 조건 |
|---|---|---|---|---|
| 01 | tee_ta_ctx | TA load/create | session open/invoke | ref_count와 flags에 따라 마지막 session 뒤 destroy 또는 keep-alive된다. |
| 02 | user_ta_ctx/ts_ctx | user TA loader | MMU/EL0 entry | TA address space, code/data, loader state를 소유한다. |
| 03 | tee_ta_session | open session | invoke/close/cancel | client open_sessions list에 연결되고 close가 남은 ref 1개를 확인한 뒤 해제한다. |
| 04 | session ref_count/refc_cv | get/put session | close와 concurrent invoke | unlink 뒤 다른 ref가 빠져 close caller의 1개만 남을 때까지 기다린다. |
| 05 | tee_ctxes global list | context creation | UUID lookup/reuse | context lock 아래 공개·제거되며 half-initialized object를 노출하면 안 된다. |
| 06 | TA private session context | TA OpenSession entry | Invoke/CloseSession | TA가 반환한 pointer를 session이 소유하고 close까지 유지한다. |
함수 내부 실행 순서
소스의 큰 분기와 side effect를 실행 순서대로 다시 펼쳤다. breakpoint는 이 목록의 경계에 두고, 다음 번호로 넘어갈 때 새로 유효해진 객체를 기록한다.
- 01
open request의 UUID, client identity와 parameter가
tee_ta_open_session()에 들어온다. - 02
tee_ta_init_session()이 새tee_ta_session을calloc()하고 condvar·초기 refcount 1·현재lock_thread를 설정한다. - 03
tee_ta_mutex아래 ID를 고르고TAILQ_INSERT_TAIL(open_sessions, s, link)로 세션을 먼저 연결한다. - 04
같은 세션에 기존 context를 연결하거나 secure partition → pseudo TA → user TA 순서로 초기화한다. 실패하면 삽입된 세션을 제거·해제한다.
- 05
check_params()와 busy 규칙을 거쳐 TAenter_open_session()을 호출한다. 성공 후에야*sess_id를 기록하고 초기 ref를 내려놓는다. - 06
invoke는 client와 parameter를 확인하고 TA context의 busy 규칙 아래
enter_invoke_cmd()를 호출한다. - 07
close는 exclusive ref와 client identity를 확인한다. 정상 context면 TA
enter_close_session()을 먼저 호출한다. - 08
이어
destroy_session()이unlink=true로 새 접근을 막고 다른 ref가 빠져ref_count == 1이 되길 기다린 후 목록에서 제거·free한다. - 09
그 뒤 context ref를 줄이고 single-instance·keep-alive·panic flag에 따라 context를 유지하거나 파괴한다.
빌드 설정이 바꾸는 실제 코드 경로
동일한 함수 이름이라도 아래 설정에 따라 포함되는 source, 구조체 크기, 인자 의미와 failure path가 달라진다.
| # | 설정 | 바뀌는 동작 | 확인 방법 |
|---|---|---|---|
| 01 | CFG_WITH_USER_TA | signed user TA load와 EL0 context를 지원한다. | pseudo TA와 user TA 생성/destruction 경로를 구분한다. |
| 02 | CFG_REE_FS_TA/CFG_EARLY_TA | TA binary source와 load 시점이 달라진다. | 동일 UUID source 우선순위와 signature 검증을 확인한다. |
| 03 | TA_FLAG_SINGLE_INSTANCE/MULTI_SESSION/KEEP_ALIVE | context 공유와 session 동시성 정책을 바꾼다. | TA header flag와 manager branch를 기록한다. |
| 04 | CFG_TA_ASLR | user TA mapping base가 session/context 생성 때 달라진다. | panic 주소 decode에 TA load base를 남긴다. |
증상에서 최초 불일치 지점까지 추적하기
마지막 panic 메시지가 아니라 어디에서 멈추고 무엇을 읽어 어떤 결론을 내릴지 정리했다. 정상값과 실패값은 같은 build와 같은 위치에서 비교한다.
| # | 관찰 증상 | 중단 위치 | 기록할 값 | 판정 |
|---|---|---|---|---|
| 01 | close와 invoke 동시 실행에서 crash | tee_ta_get/put_session과 close | session refcount, list membership, exclusive flag | session을 너무 일찍 unlink/free하는지 확인한다. |
| 02 | single-instance TA가 두 context로 load | global context lookup | UUID, flags, tee_ctxes lock | lookup/insert race 또는 flag parse 문제를 찾는다. |
| 03 | 마지막 close 뒤 secure memory 누수 | context destroy | session count, ctx ref_count, mapping/fobj | keep-alive 정책과 cleanup 누락을 구분한다. |
| 04 | 다른 client가 session ID를 사용 | session lookup | open_sessions owner, client/guest identity | session ID namespace와 list 선택 오류를 판정한다. |
OBJECT LIFETIME
session과 context의 별도 수명
| 대상 | 생성·연결 | 성공 뒤 수명 | 정리 조건 |
|---|---|---|---|
tee_ta_session | context 탐색 전 calloc()·client 목록 삽입 | 성공한 open 뒤 목록에 남음 | close의 unlink·ref 대기·free |
| session ID | 삽입 전 new_session_id() | TA open 성공 뒤 *sess_id 반환 | 실패하면 외부 성공 ID 없음 |
ts_sess.ctx | 기존 context 재사용 또는 TA 초기화 중 연결 | session이 TA context를 참조 | close 뒤 context ref 감소 |
ref_count·unlink | 초기 ref 1, get/put으로 변동 | invoke/close의 동시 접근 관리 | unlink 뒤 close ref 1만 남으면 제거 |
tee_ta_ctx | 재사용 또는 새 TA load | flag에 따라 여러 세션 공유 가능 | 마지막 ref와 keep-alive/panic 정책 |
FAILURE PATH
삽입 전후와 close 분기
| 지점 | 고정 코드의 동작 | 관찰할 상태 |
|---|---|---|
| 할당 또는 ID 실패 | 목록 삽입 없이 반환·free | client 목록에 새 ID 없음 |
| context/SP/PTA/user TA 초기화 실패 | 이미 삽입된 세션을 TAILQ_REMOVE()·free | 임시 session 누수 없음 |
| parameter 또는 TA open entry 실패 | close_session()으로 정리 | 외부 *sess_id 미반환 |
| context 없음·panicked 상태의 close | TA close entry 없이 destroy_session() | unlink/ref 대기는 계속 수행 |
| 정상 close와 다른 활성 참조 | TA close entry 후 unlink=true, ref_count == 1 대기 | 목록 제거 전 새 get 거부 |
EVIDENCE
빌드와 런타임에서 대조할 자료
소스 순서와 실제 빌드의 manager 동작을 동일 UUID·동일 client 조건에서 비교한다.
| 목적 | 읽기 전용 자료·계측 | 판정 기준 |
|---|---|---|
| 고정 소스 | tee_ta_init_session(), close_session(), tee_ta_unlink_session() | 삽입과 context 탐색, close entry와 unlink의 순서 |
| 동일 UUID 동시 open | 세션 ID, open_sessions 목록, context flags·refcount | single-instance/multi-session 분기와 실패 unwind |
| close와 invoke 경합 | unlink, ref_count, lock_thread, condvar | 새 get 차단과 남은 close ref 1 확인 |
| 마지막 context | ctx->ref_count, keep-alive·panicked flags | 세션 free와 context destroy를 분리해 확인 |
LAB
복구 가능한 시험 환경에서 재현
- 01초기화 실패 주입
ID 실패와 context 탐색·load 실패를 나눠 목록 삽입 여부와
TAILQ_REMOVE()실행을 기록한다. - 02open entry 실패
*sess_id미반환과close_session()정리 순서를 같은 세션 주소로 추적한다. - 03invoke와 close 경합
exclusive close ref, TA close entry,
unlink=true,ref_count == 1, 목록 제거 시점을 차례로 기록한다.
PRIMARY REFERENCES