QUESTION
명령마다 매개변수를 처리하는 방법이 왜 다른가?
__tee_entry_std(arg, num_params)는 먼저 foreign interrupt를 허용한 뒤 arg->cmd를 분기한다. OPEN/INVOKE는 copy_in_params()를 사용하지만 CLOSE/CANCEL, notification, SHM 등록은 같은 변환 경로를 거치지 않는다. REGISTER_SHM은 CFG_CORE_DYN_SHM && !CFG_CORE_FFA일 때만 switch에 존재한다.
GET_PROTMEM_CONFIG도 copy_in_params()를 호출하지만 TA를 실행하는 명령은 아니다. 이 명령은 보호 메모리 설정을 조회한다. 따라서 매개변수 변환과 TA 호출을 같은 단계로 취급하면 안 된다.
결과를 확인할 곳도 두 군데다. 명령 처리 함수는 보통 공유 메시지의 arg->ret에 작업 결과를 쓴다. 세션 명령은 ret_origin도 기록하지만 모든 명령이 이 필드를 쓰는 것은 아니다. 반면 __tee_entry_std()의 반환값은 지원하지 않는 명령인지 등을 나타낸다. 이 함수가 TEE_SUCCESS를 반환했다고 해서 TA 작업까지 성공한 것은 아니다.
dispatcher 689–751 · session handlers 364–459 · SHM handlers 486–525.
STRUCTURE
하나의 switch에서 명령별 처리로 나뉜다
tee_entry_std(arg, num_params) → foreign interrupt 허용 → arg->cmd switchplatform이 weak wrapper를 override해도 보통 __tee_entry_std()로 돌아온다.Session 명령
copy_in_params() 없음.설정에 따라 포함되는 명령
CFG_CORE_DYN_SHM && !CFG_CORE_FFA에서만 포함.그 외 명령과 disabled notification은 err로 가서 dispatcher의 TEE_ERROR_NOT_IMPLEMENTED를 반환한다. 호출에 참여하는 객체도 분기마다 다르다.
CALL PATH
Session 명령 네 개의 실제 차이
OPEN_SESSION
copy_in_params최대 4개 TA 매개변수. 변환 실패 시 참조 정리로 이동한다.tee_ta_open_session호출 뒤 출력 복사와 참조 정리. 마지막에 session/ret/origin을 기록한다.INVOKE_COMMAND
CLOSE / CANCEL
num_params == 0둘 다 parameter conversion이 없다.STATE LEDGER
명령별로 필요한 상태와 반환 위치
| 명령 | 선행 검사·변환 | 핵심 작업 | 결과 기록 |
|---|---|---|---|
| OPEN_SESSION | 2 meta params 검사; 나머지 ≤4개 변환 | TA session 열기 | arg->session/ret/ret_origin |
| INVOKE_COMMAND | 모든 params ≤4개 변환; session lookup | TA command 실행 | arg->ret/ret_origin |
| CLOSE_SESSION / CANCEL | num_params==0 | close / cancel | arg->ret/ret_origin |
| REGISTER / UNREGISTER_SHM | DYN_SHM && !FFA; 각각 전용 param 계약 | registered mobj 생성 / cookie로 해제 | 주로 arg->ret |
| GET_PROTMEM_CONFIG | DYN_PROTMEM; 매개변수 2개 변환 및 타입 검사 | 보호 메모리 설정 조회; TA 호출 아님 | arg->ret/ret_origin, 조회 결과 |
| DO_BOTTOM_HALF / STOP_ASYNC_NOTIF | ASYNC_NOTIF enabled | notification 전달 | switch 반환; disabled면 NOT_IMPLEMENTED |
| 기타 / 비활성 명령 | case 없음 | Unknown cmd 로그 | dispatcher TEE_ERROR_NOT_IMPLEMENTED |
VISUAL WALKTHROUGH
메시지의 매개변수 배치와 두 종류의 결과
copy_in_params: META 거부, 최대 4개tee_ta_paramvalue 복사 또는 mobj 획득, TA session 호출| wire type | 설정 | secure 표현·경계 |
|---|---|---|
| VALUE / NONE | 공통 | value a/b 복사 또는 NONE; META bit 거부 |
| TMEM | !CFG_CORE_FFA | noncontig/기본 SHM/SDP/protected memory에 따른 mobj |
| RMEM | CFG_CORE_DYN_SHM && !CFG_CORE_FFA | cookie lookup, offset+size bounds 검사 |
| FMEM | CFG_CORE_FFA | FF-A global ID/cookie, offset+size bounds 검사 |
FMEM 타입 값은 RMEM과 같다. 따라서 FMEM의 출력 크기도 RMEM case에서 복사하며, CFG_CORE_DYN_SHM으로 포함된 RMEM 정리 case는 FMEM 타입에도 해당한다. 추가 속성 비트와 빌드 설정을 확인해야 하며, REGISTER_SHM의 등록 객체 수명과 혼동하지 않는다. copy/cleanup 184–318.
알려진 명령
arg->ret에 기록 가능.resswitch 뒤 기본값 TEE_SUCCESS 반환.알 수 없거나 비활성
default/errUnknown cmd 로그, res=TEE_ERROR_NOT_IMPLEMENTED.arg->ret 기록을 가정하지 않는다.SHM handler는 arg->ret_origin을 항상 쓰지 않는다. Normal World에서 결과를 해석할 때 함수 반환과 message header 필드를 혼동하지 않는다.
UPSTREAM SOURCE
원본 코드
아래 코드는 OP-TEE OS 4.10.0의 core/tee/entry_std.c에서 665-753줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.
665
666out_dec_map:
667 mobj_dec_map(param.u[1].mem.mobj);
668out_cleanup:
669 cleanup_shm_refs(saved_attr, ¶m, num_params);
670out:
671 arg->ret = res;
672}
673
674void nsec_sessions_list_head(struct tee_ta_session_head **open_sessions)
675{
676 *open_sessions = &tee_open_sessions;
677}
678
679/* Note: this function is weak to let platforms add special handling */
680TEE_Result __weak tee_entry_std(struct optee_msg_arg *arg, uint32_t num_params)
681{
682 return __tee_entry_std(arg, num_params);
683}
684
685/*
686 * If tee_entry_std() is overridden, it's still supposed to call this
687 * function.
688 */
689TEE_Result __tee_entry_std(struct optee_msg_arg *arg, uint32_t num_params)
690{
691 TEE_Result res = TEE_SUCCESS;
692
693 /* Enable foreign interrupts for STD calls */
694 thread_set_foreign_intr(true);
695 switch (arg->cmd) {
696 case OPTEE_MSG_CMD_OPEN_SESSION:
697 entry_open_session(arg, num_params);
698 break;
699 case OPTEE_MSG_CMD_CLOSE_SESSION:
700 entry_close_session(arg, num_params);
701 break;
702 case OPTEE_MSG_CMD_INVOKE_COMMAND:
703 entry_invoke_command(arg, num_params);
704 break;
705 case OPTEE_MSG_CMD_CANCEL:
706 entry_cancel(arg, num_params);
707 break;
708#if defined(CFG_CORE_DYN_SHM) && !defined(CFG_CORE_FFA)
709 case OPTEE_MSG_CMD_REGISTER_SHM:
710 register_shm(arg, num_params);
711 break;
712 case OPTEE_MSG_CMD_UNREGISTER_SHM:
713 unregister_shm(arg, num_params);
714 break;
715#endif
716 case OPTEE_MSG_CMD_DO_BOTTOM_HALF:
717 if (IS_ENABLED(CFG_CORE_ASYNC_NOTIF))
718 notif_deliver_event(NOTIF_EVENT_DO_BOTTOM_HALF);
719 else
720 goto err;
721 break;
722 case OPTEE_MSG_CMD_STOP_ASYNC_NOTIF:
723 if (IS_ENABLED(CFG_CORE_ASYNC_NOTIF))
724 notif_deliver_event(NOTIF_EVENT_STOPPED);
725 else
726 goto err;
727 break;
728#ifdef CFG_CORE_DYN_PROTMEM
729 case OPTEE_MSG_CMD_GET_PROTMEM_CONFIG:
730 get_protmem_config(arg, num_params);
731 break;
732#ifdef CFG_CORE_FFA
733 case OPTEE_MSG_CMD_ASSIGN_PROTMEM:
734 assign_protmem(arg, num_params);
735 break;
736#else
737 case OPTEE_MSG_CMD_LEND_PROTMEM:
738 lend_protmem(arg, num_params);
739 break;
740 case OPTEE_MSG_CMD_RECLAIM_PROTMEM:
741 reclaim_protmem(arg, num_params);
742 break;
743#endif /*!CFG_CORE_FFA*/
744#endif /*CFG_CORE_DYN_PROTMEM*/
745 default:
746err:
747 EMSG("Unknown cmd 0x%x", arg->cmd);
748 res = TEE_ERROR_NOT_IMPLEMENTED;
749 }
750
751 return res;
752}
753
READING CONTRACT
발췌 범위와 읽기 기준
위 메인 발췌는 entry_std.c:665–753이며 __tee_entry_std() 본문은 689행부터다. 앞부분은 다른 함수의 끝과 weak wrapper를 포함한다. 관련 원본 두 조각은 OPEN handler 354–434와 copy_in_params() 주변 174–276이다.
원본 발췌만으로 보이지 않는 CLOSE/CANCEL, REGISTER_SHM, copy-out, cleanup 전체를 264–318, 410–525와 함께 읽는다. 모든 case가 TA manager를 호출하거나 동일한 결과 필드를 기록한다는 가정은 두지 않는다.
RELATED SOURCE
함께 읽어야 하는 원본 코드
첫 코드 조각만으로 동작이 완성되지 않는 경우 호출 매크로, 객체 정의와 실제 실행 목록을 같은 페이지에 묶었다. 각 조각은 같은 기준 commit에서 가져왔다.
01. OPEN_SESSION parameter와 TA session 생성
core/tee/entry_std.c 354-434줄이다.
354 (void *)¶ms[1].u.value);
355 break;
356 default:
357 return TEE_ERROR_BAD_PARAMETERS;
358 }
359
360 *num_meta = 2;
361 return TEE_SUCCESS;
362}
363
364static void entry_open_session(struct optee_msg_arg *arg, uint32_t num_params)
365{
366 TEE_Result res = TEE_ERROR_GENERIC;
367 TEE_ErrorOrigin err_orig = TEE_ORIGIN_TEE;
368 uint32_t sess_id = 0;
369 TEE_Identity clnt_id = { };
370 TEE_UUID uuid = { };
371 struct tee_ta_param param = { };
372 size_t num_meta = 0;
373 uint64_t saved_attr[TEE_NUM_PARAMS] = { 0 };
374
375 res = get_open_session_meta(num_params, arg->params, &num_meta, &uuid,
376 &clnt_id);
377 if (res != TEE_SUCCESS)
378 goto out;
379
380 res = copy_in_params(arg->params + num_meta, num_params - num_meta,
381 ¶m, saved_attr);
382 if (res != TEE_SUCCESS)
383 goto cleanup_shm_refs;
384
385 res = tee_ta_open_session(&err_orig, &sess_id, &tee_open_sessions,
386 &uuid, &clnt_id, TEE_TIMEOUT_INFINITE,
387 ¶m);
388 if (res)
389 sess_id = 0;
390 copy_out_param(¶m, num_params - num_meta, arg->params + num_meta,
391 saved_attr);
392
393 /*
394 * The occurrence of open/close session command is usually
395 * un-predictable, using this property to increase randomness
396 * of prng
397 */
398 plat_prng_add_jitter_entropy(CRYPTO_RNG_SRC_JITTER_SESSION,
399 &session_pnum);
400
401cleanup_shm_refs:
402 cleanup_shm_refs(saved_attr, ¶m, num_params - num_meta);
403
404out:
405 arg->session = sess_id;
406 arg->ret = res;
407 arg->ret_origin = err_orig;
408}
409
410static void entry_close_session(struct optee_msg_arg *arg, uint32_t num_params)
411{
412 TEE_Result res = TEE_SUCCESS;
413
414 if (num_params) {
415 res = TEE_ERROR_BAD_PARAMETERS;
416 goto out;
417 }
418
419 plat_prng_add_jitter_entropy(CRYPTO_RNG_SRC_JITTER_SESSION,
420 &session_pnum);
421
422 res = tee_ta_close_session(arg->session, &tee_open_sessions,
423 NSAPP_IDENTITY);
424out:
425 arg->ret = res;
426 arg->ret_origin = TEE_ORIGIN_TEE;
427}
428
429static void entry_invoke_command(struct optee_msg_arg *arg, uint32_t num_params)
430{
431 TEE_Result res;
432 TEE_ErrorOrigin err_orig = TEE_ORIGIN_TEE;
433 struct tee_ta_session *s;
434 struct tee_ta_param param = { 0 };
02. wire parameter를 secure tee_ta_param으로 복사
core/tee/entry_std.c 174-276줄이다.
174 */
175 if (ADD_OVERFLOW(mem->offs, mem->size, &req_size) ||
176 mem->mobj->size < req_size)
177 return TEE_ERROR_SECURITY;
178
179 return TEE_SUCCESS;
180}
181#endif /*CFG_CORE_DYN_SHM*/
182#endif /*!CFG_CORE_FFA*/
183
184static TEE_Result copy_in_params(const struct optee_msg_param *params,
185 uint32_t num_params,
186 struct tee_ta_param *ta_param,
187 uint64_t *saved_attr)
188{
189 TEE_Result res;
190 size_t n;
191 uint8_t pt[TEE_NUM_PARAMS] = { 0 };
192
193 if (num_params > TEE_NUM_PARAMS)
194 return TEE_ERROR_BAD_PARAMETERS;
195
196 memset(ta_param, 0, sizeof(*ta_param));
197
198 for (n = 0; n < num_params; n++) {
199 uint32_t attr;
200
201 saved_attr[n] = READ_ONCE(params[n].attr);
202
203 if (saved_attr[n] & OPTEE_MSG_ATTR_META)
204 return TEE_ERROR_BAD_PARAMETERS;
205
206 attr = saved_attr[n] & OPTEE_MSG_ATTR_TYPE_MASK;
207 switch (attr) {
208 case OPTEE_MSG_ATTR_TYPE_NONE:
209 pt[n] = TEE_PARAM_TYPE_NONE;
210 break;
211 case OPTEE_MSG_ATTR_TYPE_VALUE_INPUT:
212 case OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT:
213 case OPTEE_MSG_ATTR_TYPE_VALUE_INOUT:
214 pt[n] = TEE_PARAM_TYPE_VALUE_INPUT + attr -
215 OPTEE_MSG_ATTR_TYPE_VALUE_INPUT;
216 ta_param->u[n].val.a = READ_ONCE(params[n].u.value.a);
217 ta_param->u[n].val.b = READ_ONCE(params[n].u.value.b);
218 break;
219#ifdef CFG_CORE_FFA
220 case OPTEE_MSG_ATTR_TYPE_FMEM_INPUT:
221 case OPTEE_MSG_ATTR_TYPE_FMEM_OUTPUT:
222 case OPTEE_MSG_ATTR_TYPE_FMEM_INOUT:
223 res = set_fmem_param(¶ms[n].u.fmem,
224 &ta_param->u[n].mem);
225 if (res)
226 return res;
227 pt[n] = TEE_PARAM_TYPE_MEMREF_INPUT + attr -
228 OPTEE_MSG_ATTR_TYPE_FMEM_INPUT;
229 break;
230#else /*!CFG_CORE_FFA*/
231 case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
232 case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
233 case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
234 res = set_tmem_param(¶ms[n].u.tmem, saved_attr[n],
235 &ta_param->u[n].mem);
236 if (res)
237 return res;
238 pt[n] = TEE_PARAM_TYPE_MEMREF_INPUT + attr -
239 OPTEE_MSG_ATTR_TYPE_TMEM_INPUT;
240 break;
241#ifdef CFG_CORE_DYN_SHM
242 case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
243 case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
244 case OPTEE_MSG_ATTR_TYPE_RMEM_INOUT:
245 res = set_rmem_param(¶ms[n].u.rmem,
246 &ta_param->u[n].mem);
247 if (res)
248 return res;
249 pt[n] = TEE_PARAM_TYPE_MEMREF_INPUT + attr -
250 OPTEE_MSG_ATTR_TYPE_RMEM_INPUT;
251 break;
252#endif /*CFG_CORE_DYN_SHM*/
253#endif /*!CFG_CORE_FFA*/
254 default:
255 return TEE_ERROR_BAD_PARAMETERS;
256 }
257 }
258
259 ta_param->types = TEE_PARAM_TYPES(pt[0], pt[1], pt[2], pt[3]);
260
261 return TEE_SUCCESS;
262}
263
264static void cleanup_shm_refs(const uint64_t *saved_attr,
265 struct tee_ta_param *param, uint32_t num_params)
266{
267 size_t n;
268
269 for (n = 0; n < MIN((unsigned int)TEE_NUM_PARAMS, num_params); n++) {
270 switch (saved_attr[n]) {
271 case OPTEE_MSG_ATTR_TYPE_TMEM_INPUT:
272 case OPTEE_MSG_ATTR_TYPE_TMEM_OUTPUT:
273 case OPTEE_MSG_ATTR_TYPE_TMEM_INOUT:
274#ifdef CFG_CORE_DYN_SHM
275 case OPTEE_MSG_ATTR_TYPE_RMEM_INPUT:
276 case OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT:
DETAILS
분기마다 다른 검증·자원 경계
OPEN의 두 meta 값
get_open_session_meta()는 최소 두 개와 각 attr의 정확한 META|VALUE_INPUT 조합을 검사한다. UUID와 login/identity를 읽고 나서 남은 parameter만 copy_in_params()에 준다.
INVOKE의 session 참조
변환 성공 뒤 tee_ta_get_session()으로 현재 open-session 목록에서 ID를 찾는다. 실패 시 BAD_PARAMETERS, 성공 시 TA invoke 후 tee_ta_put_session()을 거쳐 output 복사와 mobj cleanup을 한다.
SHM 등록은 별도 ABI
REGISTER_SHM은 한 개의 TMEM_OUTPUT|NONCONTIG와 noncontiguous list를 검사한다. UNREGISTER_SHM은 cookie로 registered mobj를 해제한다. 두 case는 CFG_CORE_DYN_SHM && !CFG_CORE_FFA에서만 컴파일된다.
IMPLEMENTATION NOTES
parameter 변환을 모든 명령에 대입하지 않기
최대 네 개의 TA parameter
copy_in_params()는 받은 num_params > TEE_NUM_PARAMS를 먼저 거절한다. OPEN에서는 두 meta를 제외한 개수이고 INVOKE에서는 전체 개수다. saved_attr를 저장하고 META bit를 거절한다.
빌드별 memory type
일반 경로는 TMEM 및 조건부 RMEM을, FF-A 경로는 FMEM을 처리한다. RMEM/FMEM은 cookie나 global ID로 mobj를 얻고 offset+size overflow 또는 객체 범위를 확인한다. TMEM의 noncontiguous와 기본 shared-memory 경로는 별도다.
FMEM도 타입 값과 구조체 배치를 함께 확인한다
OPTEE_MSG_ATTR_TYPE_FMEM_*는 RMEM 타입의 별칭이다. copy_out_param()의 RMEM 분기는 FMEM 출력에도 해당하며, 두 구조체의 size는 같은 위치에 있다. cleanup_shm_refs()의 RMEM case는 CFG_CORE_DYN_SHM일 때 포함되므로, 이 설정에서 FMEM 타입 값도 같은 case에 대응한다. 이름만 보고 FMEM이 별도 정리 경로를 거친다고 판단하면 안 된다.
입력 변환과 출력 복사는 타입 마스크를 쓰지만 참조 정리는 saved_attr[n] 전체 값으로 분기한다. 따라서 NONCONTIG처럼 추가 비트가 있는 속성까지 전부 같은 정리 case에 들어간다고 일반화하지 않는다. REGISTER_SHM의 등록 객체 수명도 TA 호출 중의 임시 참조와 구분한다.
DEEP ANALYSIS
고정 소스의 결정 표
| 경계 | 조건 | 결과·주의 |
|---|---|---|
copy_in_params | TA params 4개 초과 또는 META bit | TEE_ERROR_BAD_PARAMETERS; OPEN 앞 2 meta는 따로 처리 |
| RMEM/FMEM lookup | cookie/global ID 미발견 또는 offset+size overflow | BAD_PARAMETERS 또는 SECURITY; 설정별로 하나의 type군 선택 |
| OPEN/INVOKE | TA manager 결과 | handler가 arg->ret/ret_origin 기록; dispatcher 반환은 별도 |
| CLOSE/CANCEL | 0 params 아님 | BAD_PARAMETERS; parameter copy 없음 |
| REGISTER_SHM | DYN_SHM && !FFA; 1개 TMEM_OUTPUT|NONCONTIG | registered mobj를 만들고 arg->ret 기록 |
| notification | ASYNC_NOTIF 꺼짐 | err → 함수 반환 NOT_IMPLEMENTED |
| unknown cmd | switch case 없음 | arg->ret 변경을 가정하지 않고 함수 반환 NOT_IMPLEMENTED |
OBJECT LIFETIME
각 분기가 소유하는 상태
| 대상 | 만드는/읽는 곳 | 정리·유지 |
|---|---|---|
optee_msg_arg | caller가 전달; dispatcher/handler가 cmd·params·header 접근 | Normal World와 공유되는 call message |
tee_ta_param | OPEN/INVOKE의 stack local | handler 종료 시 수명 끝 |
saved_attr[] | copy_in에서 각 attr 기록 | copy_out·cleanup 판단에 사용 |
mobj 참조 | TMEM/RMEM/FMEM 변환 | 설정·type에 맞는 함수가 정리 |
tee_open_sessions | OPEN이 session 추가; INVOKE/CLOSE/CANCEL이 ID 조회 | 단일 call의 임시 배열과 달리 session 수명까지 유지 |
arg->ret / 함수 반환 | handler / dispatcher | 서로 다른 두 결과 채널 |
FAILURE PATH
어느 분기가 먼저 거절하는가?
| 증상 | 처음 검사할 조건 | 고정 소스 |
|---|---|---|
| OPEN BAD_PARAMETERS | meta 2개와 정확한 attr; 남은 TA params ≤4 | 327–383 |
| INVOKE BAD_PARAMETERS | TA params ≤4, type, session ID lookup | 184–261, 429–459 |
| memory type 거절 | FFA면 FMEM, non-FFA면 TMEM/조건부 RMEM; bounds | 66–179, 219–255 |
| REGISTER_SHM 실패/명령 없음 | DYN_SHM && !FFA, 한 개 TMEM_OUTPUT|NONCONTIG | 486–525, 708–715 |
| 알 수 없는 명령 | case/설정 유무; 함수 반환 NOT_IMPLEMENTED | 716–751 |
이 표는 실제 반환 조건을 보여 준다. 모든 실패를 ‘cleanup 중복으로 UAF’로 묶는 진단은 이 코드로 뒷받침되지 않는다.
EVIDENCE
대상 빌드에서 대조할 자료
이 리뷰는 소스와 로컬 페이지를 검증했으며 OP-TEE를 새로 빌드하거나 보드에서 실행하지 않았다.
| 자료 | 읽기 예시 | 구분할 값 |
|---|---|---|
| 빌드 설정 | CFG_CORE_FFA, CFG_CORE_DYN_SHM, CFG_CORE_ASYNC_NOTIF | 어느 switch case와 memory type이 포함되는지 |
| symbol | ${CROSS_COMPILE64}nm -n out/arm/core/tee.elf | grep -E ' (__tee_entry_std|entry_open_session|entry_invoke_command)$' | 소스 선언문이 아닌 symbol 이름 |
| command trace | arg.cmd, num_params, arg.ret, dispatcher 반환 | handler의 결과와 함수 반환 분리 |
| parameter trace | meta attr, saved_attr, secure TA type, mobj/cookie | OPEN의 meta 2개와 일반 TA params 분리 |
LAB
분기별 최소 검증
- 01OPEN과 INVOKE의 차이
OPEN의 두 meta attr를 바꾸거나 남은 TA params를 5개로 만들어 BAD_PARAMETERS 경계를 확인한다. INVOKE에는 meta를 넣어 별도로 비교한다.
- 02설정에 따른 SHM case
DYN_SHM/FFA 빌드 조합에서 REGISTER_SHM case의 존재와 한 개 parameter 계약을 확인한다.
- 03두 반환값
알 수 없는 cmd와 알려진 cmd의 handler 오류를 각각 넣어 dispatcher 반환,
arg->ret,ret_origin을 독립 기록한다.
PRIMARY REFERENCES