TEE-TA学习轨迹第九篇:libteec代码详细分析
libteec代码详细分析
libteec.so是TEE(Trusted Execution Environment,可信执行环境)客户端API的核心实现,遵循GlobalPlatform(GP)TEE标准,主要用于Linux用户空间(REE,富执行环境)与安全世界的TEE(如OP-TEE)进行通信,提供会话管理、共享内存操作、命令调用等核心能力。
一、代码整体背景
1. 版权与许可
代码由Linaro维护,基于BSD许可开源,适配OP-TEE(Open Portable TEE),是连接普通应用与TEE安全服务的关键层。
2. 核心依赖
- 系统调用:fcntl(文件控制)、ioctl(设备控制)、mmap(内存映射)、pthread(线程安全)等;
- TEE标准头文件:tee_client_api.h(GP TEE客户端API定义)、linux/tee.h(Linux内核TEE驱动接口);
- 调试/日志:teec_trace.h(TEEC日志宏,如EMSG)。
二、关键宏与常量定义
宏/常量 | 用途 |
__aligned(x) | 封装编译器的内存对齐属性( __attribute__((aligned(x))) ) |
MIN(x,y) | 取两个值的最小值 |
TEEC_MAX_DEV_SEQ | 尝试打开TEE设备文件的最大序列(遍历 /dev/tee0 到 /dev/tee9 ) |
MEMREF_SHM_ID/Offs/Size | 操作 struct tee_ioctl_param 中内存引用的SHM ID、偏移、大小字段 |
SHM_FLAG_* | 共享内存内部标志( BUFFER_ALLOCED :缓冲区已分配; SHADOW_BUFFER_ALLOCED :影子缓冲区已分配) |
三、静态辅助函数(工具层)
1. 线程安全:锁封装
static void teec_mutex_lock(pthread_mutex_t *mu) { pthread_mutex_lock(mu); } static void teec_mutex_unlock(pthread_mutex_t *mu) { pthread_mutex_unlock(mu); }- 封装pthread互斥锁的加/解锁,保证多线程访问TEE资源时的安全性。
2. 内存分配:页对齐分配
static void *teec_paged_aligned_alloc(size_t sz) { void *p = NULL; size_t page_sz = sysconf(_SC_PAGESIZE); // 获取系统页大小 size_t aligned_sz = ((sz + page_sz - 1) / page_sz) * page_sz; // 向上取整到页大小 if (aligned_sz >= sz && !posix_memalign(&p, page_sz, aligned_sz)) return p; return NULL; }- 按系统页大小对齐分配内存(TEE共享内存要求页对齐),避免内存对齐导致的访问错误。
3. 设备打开:验证TEE兼容性
static int teec_open_dev(const char *devname, const char *capabilities, uint32_t *gen_caps) { int fd = 0; struct tee_ioctl_version_data vers; memset(&vers, 0, sizeof(vers)); fd = open(devname, O_RDWR); if (fd < 0) return -1; if (ioctl(fd, TEE_IOC_VERSION, &vers)) { EMSG("TEE_IOC_VERSION failed"); goto err; } /* We can only handle GP TEEs */ if (!(vers.gen_caps & TEE_GEN_CAP_GP)) goto err; if (capabilities) { if (strcmp(capabilities, "optee-tz") == 0) { if (vers.impl_id != TEE_IMPL_ID_OPTEE) goto err; if (!(vers.impl_caps & TEE_OPTEE_CAP_TZ)) goto err; } else { /* Unrecognized capability requested */ goto err; } } *gen_caps = vers.gen_caps; return fd; err: close(fd); return -1; }- 打开TEE设备文件(如/dev/tee0),通过TEE_IOC_VERSION ioctl验证:
- 必须是GP(GlobalPlatform)兼容的TEE;
- 若指定capabilities="optee-tz",需验证是OP-TEE实现且支持TZ(Trusted Zone);
- 返回设备FD和TEE通用能力(gen_caps)。
4. 共享内存:分配/注册
static int teec_shm_alloc(int fd, size_t size, int *id); // 向TEE申请分配共享内存 static int
static int teec_shm_alloc(int fd, size_t size, int *id) { int shm_fd = 0; struct tee_ioctl_shm_alloc_data data; memset(&data, 0, sizeof(data)); data.size = size; shm_fd = ioctl(fd, TEE_IOC_SHM_ALLOC, &data); if (shm_fd < 0) return -1; *id = data.id; return shm_fd; }teec_shm_register(int fd, void *buf, size_t size, int *id); // 注册用户内存到TEE
static int teec_shm_register(int fd, void *buf, size_t size, int *id) { int shm_fd = 0; struct tee_ioctl_shm_register_data data; memset(&data, 0, sizeof(data)); data.addr = (uintptr_t)buf; data.length = size; shm_fd = ioctl(fd, TEE_IOC_SHM_REGISTER, &data); if (shm_fd < 0) return -1; *id = data.id; return shm_fd; }- 通过TEE_IOC_SHM_ALLOC/TEE_IOC_SHM_REGISTER ioctl与TEE内核交互,返回共享内存FD和ID。
四、核心API实现(GP标准接口)
1. 上下文管理:初始化/销毁
TEEC_InitializeContext
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx)
TEEC_Result TEEC_InitializeContext(const char *name, TEEC_Context *ctx) { char devname[PATH_MAX] = { 0 }; int fd = 0; size_t n = 0; if (!ctx) return TEEC_ERROR_BAD_PARAMETERS; for (n = 0; n < TEEC_MAX_DEV_SEQ; n++) { uint32_t gen_caps = 0; snprintf(devname, sizeof(devname), "/dev/tee%zu", n); fd = teec_open_dev(devname, name, &gen_caps); if (fd >= 0) { ctx->imp.fd = fd; ctx->imp.reg_mem = gen_caps & TEE_GEN_CAP_REG_MEM; ctx->imp.memref_null = gen_caps & TEE_GEN_CAP_MEMREF_NULL; return TEEC_SUCCESS; } } return TEEC_ERROR_ITEM_NOT_FOUND; }- 功能:初始化TEE上下文(与TEE设备建立连接);
- 逻辑:
- 遍历/dev/tee0到/dev/tee9,调用teec_open_dev打开兼容的TEE设备;
- 记录设备FD、TEE能力(如是否支持注册内存、空内存引用);
- 失败返回TEEC_ERROR_ITEM_NOT_FOUND(无可用TEE设备)。
TEEC_FinalizeContext
void TEEC_FinalizeContext(TEEC_Context *ctx)
void TEEC_FinalizeContext(TEEC_Context *ctx) { if (ctx) close(ctx->imp.fd); }- 功能:销毁TEE上下文,关闭设备FD。
2. 共享内存管理
TEEC_AllocateSharedMemory
TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm) { int fd = 0; size_t s = 0; if (!ctx || !shm) return TEEC_ERROR_BAD_PARAMETERS; if (!shm->flags || (shm->flags & ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))) return TEEC_ERROR_BAD_PARAMETERS; s = shm->size; if (!s) s = 8; if (ctx->imp.reg_mem) { shm->buffer = teec_paged_aligned_alloc(s); if (!shm->buffer) return TEEC_ERROR_OUT_OF_MEMORY; fd = teec_shm_register(ctx->imp.fd, shm->buffer, s, &shm->imp.id); if (fd < 0) { free(shm->buffer); shm->buffer = NULL; return TEEC_ERROR_OUT_OF_MEMORY; } shm->imp.registered_fd = fd; } else { fd = teec_shm_alloc(ctx->imp.fd, s, &shm->imp.id); if (fd < 0) return TEEC_ERROR_OUT_OF_MEMORY; shm->buffer = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (shm->buffer == (void *)MAP_FAILED) { shm->imp.id = -1; return TEEC_ERROR_OUT_OF_MEMORY; } shm->imp.registered_fd = -1; } shm->imp.shadow_buffer = NULL; shm->imp.alloced_size = s; shm->imp.flags = SHM_FLAG_BUFFER_ALLOCED; return TEEC_SUCCESS; }- 功能:由TEE内核分配共享内存(而非用户提供);
- 逻辑:
- 按页对齐分配内存,注册到TEE;
- 若TEE不支持注册内存,通过mmap映射TEE分配的内存到用户空间。
TEEC_ReleaseSharedMemory
void TEEC_ReleaseSharedMemory(TEEC_SharedMemory *shm)
- 功能:释放共享内存,清理影子缓冲区/映射内存,关闭FD,重置元信息。
TEEC_RegisterSharedMemory
TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context *ctx, TEEC_SharedMemory *shm) { TEEC_Result res = TEEC_SUCCESS; int fd = 0; size_t s = 0; if (!ctx || !shm) return TEEC_ERROR_BAD_PARAMETERS; if (!shm->flags || (shm->flags & ~(TEEC_MEM_INPUT | TEEC_MEM_OUTPUT))) return TEEC_ERROR_BAD_PARAMETERS; if (!shm->buffer) return TEEC_ERROR_BAD_PARAMETERS; s = shm->size; if (!s) s = 8; if (ctx->imp.reg_mem) { fd = teec_shm_register(ctx->imp.fd, shm->buffer, s, &shm->imp.id); if (fd >= 0) { shm->imp.registered_fd = fd; shm->imp.shadow_buffer = NULL; shm->imp.flags = 0; goto out; } /* * If we're here TEE_IOC_SHM_REGISTER failed, probably * because some read-only memory was supplied and the Linux * kernel doesn't like that at the moment. * * The error could also have some other origin. In any case * we're not making matters worse by trying to allocate and * register a shadow buffer before giving up. */ shm->imp.shadow_buffer = teec_paged_aligned_alloc(s); if (!shm->imp.shadow_buffer) return TEEC_ERROR_OUT_OF_MEMORY; fd = teec_shm_register(ctx->imp.fd, shm->imp.shadow_buffer, s, &shm->imp.id); if (fd >= 0) { shm->imp.registered_fd = fd; shm->imp.flags = SHM_FLAG_SHADOW_BUFFER_ALLOCED; goto out; } if (errno == ENOMEM) res = TEEC_ERROR_OUT_OF_MEMORY; else res = TEEC_ERROR_GENERIC; free(shm->imp.shadow_buffer); shm->imp.shadow_buffer = NULL; return res; } else { fd = teec_shm_alloc(ctx->imp.fd, s, &shm->imp.id); if (fd < 0) return TEEC_ERROR_OUT_OF_MEMORY; shm->imp.shadow_buffer = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); close(fd); if (shm->imp.shadow_buffer == (void *)MAP_FAILED) { shm->imp.id = -1; return TEEC_ERROR_OUT_OF_MEMORY; } shm->imp.registered_fd = -1; shm->imp.flags = 0; } out: shm->imp.alloced_size = s; return TEEC_SUCCESS; }- 功能:将用户空间的内存注册到TEE,供安全世界访问;
- 关键逻辑:
- 校验参数(上下文/共享内存非空、标志合法、缓冲区非空);
- 若TEE支持内存注册(reg_mem),直接注册用户内存;
- 若注册失败(如用户内存只读),分配影子缓冲区(可写),注册影子缓冲区并拷贝用户数据;
- 记录共享内存ID、FD、影子缓冲区等元信息。
3. 会话管理
TEEC_OpenSession
TEEC_Result TEEC_OpenSession(TEEC_Context *ctx, TEEC_Session *session, const TEEC_UUID *destination, uint32_t connection_method, const void *connection_data, TEEC_Operation *operation, uint32_t *ret_origin) { struct tee_ioctl_open_session_arg *arg = NULL; struct tee_ioctl_param *params = NULL; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eorig = 0; int rc = 0; const size_t arg_size = sizeof(struct tee_ioctl_open_session_arg) + TEEC_CONFIG_PAYLOAD_REF_COUNT * sizeof(struct tee_ioctl_param); union { struct tee_ioctl_open_session_arg arg; uint8_t data[arg_size]; } buf; struct tee_ioctl_buf_data buf_data; TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT]; memset(&buf, 0, sizeof(buf)); memset(&shm, 0, sizeof(shm)); memset(&buf_data, 0, sizeof(buf_data)); if (!ctx || !session) { eorig = TEEC_ORIGIN_API; res = TEEC_ERROR_BAD_PARAMETERS; goto out; } buf_data.buf_ptr = (uintptr_t)&buf; buf_data.buf_len = sizeof(buf); arg = &buf.arg; arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params = (struct tee_ioctl_param *)(arg + 1); uuid_to_octets(arg->uuid, destination); setup_client_data(arg, connection_method, connection_data); res = teec_pre_process_operation(ctx, operation, params, shm); if (res != TEEC_SUCCESS) { eorig = TEEC_ORIGIN_API; goto out_free_temp_refs; } rc = ioctl(ctx->imp.fd, TEE_IOC_OPEN_SESSION, &buf_data); if (rc) { EMSG("TEE_IOC_OPEN_SESSION failed"); eorig = TEEC_ORIGIN_COMMS; res = ioctl_errno_to_res(errno); goto out_free_temp_refs; } res = arg->ret; eorig = arg->ret_origin; if (res == TEEC_SUCCESS) { session->imp.ctx = ctx; session->imp.session_id = arg->session; } teec_post_process_operation(operation, params, shm); out_free_temp_refs: teec_free_temp_refs(operation, shm); out: if (ret_origin) *ret_origin = eorig; return res; }- 功能:与TEE中的可信应用(TA)建立会话;
- 核心步骤:
- 构造tee_ioctl_open_session_arg参数(TA的UUID、客户端登录方式、参数列表);
- 预处理操作参数(转换为TEE内核可识别的格式,见下文参数处理);
- 通过TEE_IOC_OPEN_SESSION ioctl发送会话打开请求;
- 后处理参数(将TEE返回的数据拷贝回用户空间);
- 记录会话ID,返回执行结果。
TEEC_CloseSession
void TEEC_CloseSession(TEEC_Session *session) { struct tee_ioctl_close_session_arg arg; memset(&arg, 0, sizeof(arg)); if (!session) return; arg.session = session->imp.session_id; if (ioctl(session->imp.ctx->imp.fd, TEE_IOC_CLOSE_SESSION, &arg)) EMSG("Failed to close session 0x%x", session->imp.session_id); }- 功能:关闭TEE会话,通过TEE_IOC_CLOSE_SESSION ioctl通知TEE内核。
4. 命令调用
TEEC_InvokeCommand
TEEC_Result TEEC_InvokeCommand(TEEC_Session *session, uint32_t cmd_id, TEEC_Operation *operation, uint32_t *error_origin) { struct tee_ioctl_invoke_arg *arg = NULL; struct tee_ioctl_param *params = NULL; TEEC_Result res = TEEC_ERROR_GENERIC; uint32_t eorig = 0; int rc = 0; const size_t arg_size = sizeof(struct tee_ioctl_invoke_arg) + TEEC_CONFIG_PAYLOAD_REF_COUNT * sizeof(struct tee_ioctl_param); union { struct tee_ioctl_invoke_arg arg; uint8_t data[arg_size]; } buf; struct tee_ioctl_buf_data buf_data; TEEC_SharedMemory shm[TEEC_CONFIG_PAYLOAD_REF_COUNT]; memset(&buf, 0, sizeof(buf)); memset(&buf_data, 0, sizeof(buf_data)); memset(&shm, 0, sizeof(shm)); if (!session) { eorig = TEEC_ORIGIN_API; res = TEEC_ERROR_BAD_PARAMETERS; goto out; } buf_data.buf_ptr = (uintptr_t)&buf; buf_data.buf_len = sizeof(buf); arg = &buf.arg; arg->num_params = TEEC_CONFIG_PAYLOAD_REF_COUNT; params = (struct tee_ioctl_param *)(arg + 1); arg->session = session->imp.session_id; arg->func = cmd_id; if (operation) { teec_mutex_lock(&teec_mutex); operation->imp.session = session; teec_mutex_unlock(&teec_mutex); } res = teec_pre_process_operation(session->imp.ctx, operation, params, shm); if (res != TEEC_SUCCESS) { eorig = TEEC_ORIGIN_API; goto out_free_temp_refs; } rc = ioctl(session->imp.ctx->imp.fd, TEE_IOC_INVOKE, &buf_data); if (rc) { EMSG("TEE_IOC_INVOKE failed"); eorig = TEEC_ORIGIN_COMMS; res = ioctl_errno_to_res(errno); goto out_free_temp_refs; } res = arg->ret; eorig = arg->ret_origin; teec_post_process_operation(operation, params, shm); out_free_temp_refs: teec_free_temp_refs(operation, shm); out: if (error_origin) *error_origin = eorig; return res; }- 功能:向已建立的TEE会话发送命令(调用TA的接口);
- 逻辑:
- 构造tee_ioctl_invoke_arg参数(会话ID、命令ID、参数列表);
- 预处理操作参数,保证线程安全(关联operation与session);
- 通过TEE_IOC_INVOKE ioctl发送命令;
- 后处理参数,返回TEE的执行结果和错误来源。
5. 操作取消
TEEC_RequestCancellation
void TEEC_RequestCancellation(TEEC_Operation *operation) { TEEC_Session *session = NULL; struct tee_ioctl_cancel_arg arg; memset(&arg, 0, sizeof(arg)); if (!operation) return; teec_mutex_lock(&teec_mutex); session = operation->imp.session; teec_mutex_unlock(&teec_mutex); if (!session) return; arg.session = session->imp.session_id; arg.cancel_id = 0; if (ioctl(session->imp.ctx->imp.fd, TEE_IOC_CANCEL, &arg)) EMSG("TEE_IOC_CANCEL: %s", strerror(errno)); }- 功能:取消正在执行的TEE操作,通过TEE_IOC_CANCEL ioctl通知TEE内核。
五、参数处理
TEE支持多种参数类型(值参数、内存引用参数),代码通过pre_process/post_process系列函数完成用户空间参数 ↔ TEE内核参数的转换:
1. 参数类型分类
参数类型 | 用途 |
TEEC_NONE | 无参数 |
TEEC_VALUE_* | 整型值参数(输入/输出/输入输出) |
TEEC_MEMREF_TEMP_* | 临时内存引用(单次命令有效,无需提前注册) |
TEEC_MEMREF_WHOLE | 整段注册内存引用(使用已注册的共享内存全部空间) |
TEEC_MEMREF_PARTIAL_* | 部分注册内存引用(使用已注册共享内存的指定偏移/大小) |
2. 预处理(teec_pre_process_*)
- 转换参数类型为TEE内核定义的tee_ioctl_param格式;
- 处理内存引用:分配临时共享内存、拷贝用户数据到影子缓冲区;
- 校验参数合法性(如偏移+大小不超过共享内存范围)。
3. 后处理(teec_post_process_*)
- 将TEE返回的结果(如输出内存数据、值参数)拷贝回用户空间;
- 更新用户传入的参数结构体(如内存大小、值参数的a/b字段)。
4. 临时内存释放(teec_free_temp_refs)
- 释放临时内存引用分配的共享内存,避免内存泄漏。
六、错误处理与辅助函数
1. ioctl_errno_to_res
static TEEC_Result ioctl_errno_to_res(int err)
- 将系统调用错误码(如ENOMEM、EINVAL)转换为GP标准的TEEC错误码(如TEEC_ERROR_OUT_OF_MEMORY、TEEC_ERROR_BAD_PARAMETERS)。
2. uuid_to_octets
static void uuid_to_octets(uint8_t d[TEE_IOCTL_UUID_LEN], const TEEC_UUID *s)
- 将TEEC_UUID结构体转换为TEE内核要求的字节数组格式。
3. setup_client_data
static void setup_client_data(struct tee_ioctl_open_session_arg *arg, uint32_t connection_method, const void *connection_data)
- 设置客户端登录方式(如公共登录、用户登录、组登录),填充会话的客户端身份信息。
七、核心功能总结
该代码实现了GP TEE客户端API的核心能力,主要解决以下需求:
- TEE设备交互:遍历并打开兼容的TEE设备,验证TEE兼容性;
- 共享内存管理:支持注册用户内存、分配TEE内存,处理内存对齐和只读内存问题(影子缓冲区);
- 会话生命周期:与TA建立/关闭会话,传递客户端身份信息;
- 命令调用:向TA发送命令,处理多类型参数的转换与数据拷贝;
- 线程安全:通过互斥锁保证多线程访问TEE资源的安全性;
- 错误标准化:将系统错误转换为GP标准错误码,便于上层应用处理。
八、典型使用流程
1. TEEC_InitializeContext → 初始化TEE上下文 2. TEEC_AllocateSharedMemory/RegisterSharedMemory → 分配/注册共享内存 3. TEEC_OpenSession → 与TA建立会话 4. TEEC_InvokeCommand → 调用TA的命令(传递参数、共享内存) 5. TEEC_CloseSession → 关闭会话 6. TEEC_ReleaseSharedMemory → 释放共享内存 7. TEEC_FinalizeContext → 销毁上下文