You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SGX Ecall中返回未知大小的指针?含[out]属性用法疑问

嘿,这两个问题在SGX开发里算是高频痛点了,我来给你拆解几个实用的解决方案,都是实际项目里验证过的:

问题1:如何在SGX Ecall中返回未知大小的指针?

SGX的Enclave和外部进程内存是完全隔离的,直接返回Enclave内部的指针外部根本访问不了,所以得用两种主流思路来实现:

方案1:两次调用(先拿长度,再取数据)

这是最稳妥、最符合SGX安全模型的方式——先调用一个Ecall获取数据的总长度,外部根据这个长度分配足够的内存,再调用第二个Ecall把数据拷贝到外部内存里。

EDL 定义

enclave {
    trusted {
        // 第一个Ecall:获取数据的长度
        size_t get_target_data_len();
        // 第二个Ecall:把数据拷贝到外部提前分配好的内存
        void fetch_target_data([out, size=len] int *data_buf, size_t len);
    };
};

Enclave 侧实现

size_t get_target_data_len() {
    // 这里可以是动态计算的长度,比如从数据库/文件读取的内容长度
    return 12;
}

void fetch_target_data(int *data_buf, size_t len) {
    // 先做长度校验,避免外部传入的内存不够
    if (len < get_target_data_len()) {
        // 可以返回错误码或者直接处理,这里简化示例
        return;
    }
    // 填充数据到外部内存
    for (int i = 0; i < get_target_data_len(); i++) {
        data_buf[i] = i * 3;
    }
}

外部进程调用

// 第一步:获取数据长度
size_t data_len = ecall_get_target_data_len(enclave_id);
// 第二步:分配对应大小的内存
int *data_buf = (int*)malloc(data_len * sizeof(int));
if (!data_buf) {
    // 处理内存分配失败
    return -1;
}
// 第三步:获取实际数据
ecall_fetch_target_data(enclave_id, data_buf, data_len);
// 使用数据...
// 最后别忘了释放内存
free(data_buf);

方案2:Enclave内分配不可信内存(用ocalloc)

SGX提供了ocalloc函数,专门用来在不可信内存区分配空间,这个内存外部进程是可以直接访问的。用这种方式可以一次Ecall完成数据返回,但要注意外部必须负责释放内存。

EDL 定义

enclave {
    trusted {
        // 返回数据指针,同时通过输出参数返回长度
        int* get_dynamic_data([out] size_t *data_len);
    };
};

Enclave 侧实现

int* get_dynamic_data(size_t *data_len) {
    *data_len = 15;
    // 在不可信内存分配空间,参数是元素个数和单个元素大小
    int* data_ptr = (int*)ocalloc(*data_len, sizeof(int));
    if (!data_ptr) {
        *data_len = 0;
        return NULL;
    }
    // 填充数据
    for (int i = 0; i < *data_len; i++) {
        data_ptr[i] = i + 10;
    }
    return data_ptr;
}

外部进程调用

size_t data_len = 0;
int *data_ptr = ecall_get_dynamic_data(enclave_id, &data_len);
if (data_ptr && data_len > 0) {
    // 使用数据...
    // 必须用free释放,因为ocalloc分配的是不可信内存
    free(data_ptr);
}
问题2:无需指定size的[out]属性实现方式

默认的[out, size=len]确实需要提前知道长度,有点受限,这里给你两个不用指定size的替代方案:

方案1:用双重指针(**)配合ocalloc

在EDL里定义双重指针,让Enclave在不可信内存中分配空间后,把指针赋值给双重指针的解引用,同时返回长度。这样[out]直接作用在双重指针上,不需要提前指定size。

EDL 定义

enclave {
    trusted {
        void fetch_dynamic_data_out([out] int **data_ptr, [out] size_t *data_len);
    };
};

Enclave 侧实现

void fetch_dynamic_data_out(int **data_ptr, size_t *data_len) {
    *data_len = 20;
    // 分配不可信内存
    *data_ptr = (int*)ocalloc(*data_len, sizeof(int));
    if (!*data_ptr) {
        *data_len = 0;
        return;
    }
    // 填充数据
    for (int i = 0; i < *data_len; i++) {
        (*data_ptr)[i] = i * 5;
    }
}

外部进程调用

int *data_ptr = NULL;
size_t data_len = 0;
ecall_fetch_dynamic_data_out(enclave_id, &data_ptr, &data_len);
if (data_ptr) {
    // 使用数据...
    free(data_ptr);
}

方案2:封装成包含指针和长度的结构体

把数据指针和长度打包成一个结构体,EDL里对整个结构体使用[out]属性,这样可以一次返回所有信息,同样不需要提前指定size。

EDL 定义

// 先定义结构体
typedef struct {
    int *data;
    size_t len;
} DynamicDataResult;

enclave {
    trusted {
        void fetch_data_struct([out] DynamicDataResult *result);
    };
};

Enclave 侧实现

void fetch_data_struct(DynamicDataResult *result) {
    result->len = 25;
    result->data = (int*)ocalloc(result->len, sizeof(int));
    if (!result->data) {
        result->len = 0;
        return;
    }
    // 填充数据
    for (int i = 0; i < result->len; i++) {
        result->data[i] = i + 20;
    }
}

外部进程调用

DynamicDataResult result = {NULL, 0};
ecall_fetch_data_struct(enclave_id, &result);
if (result.data) {
    // 使用数据...
    free(result.data);
}

注意事项

  • 用ocalloc分配的内存必须由外部进程用free释放,绝对不能在Enclave里释放(因为属于不可信内存区)。
  • 不管哪种方案,一定要做好错误处理:比如分配失败时返回NULL、设置长度为0,避免外部访问非法内存。
  • 两次调用的方案虽然多了一次Ecall,但安全性更高(外部完全控制内存),适合对安全要求高的场景;一次调用的方案更简洁,但要注意内存管理的细节。

内容的提问来源于stack exchange,提问作者savx2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:43:05