Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Documentation/devel/encfiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,14 @@ Additional details
file data. Therefore, the usual NIST limits on the total number of
invocations of the encryption operation with the same key would be reached
much slower.

- TCB migration: Gramine supports migration between different CPU SVN versions
by enabling it using the ``allow_tcb_migration`` mount parameter in the
Gramine manifest. This feature allows encrypted files to be accessed even when
the CPU SVN has changed after applying microcode updates. The current CPU SVN
is stored in the mount directory in a file named ``gramine.tcb_info``. On
startup if the CPU SVN has changed, Gramine will unseal the files using the
old CPU SVN and reseal them with the current CPU SVN. This feature can negatively
impact integrity of files, as it allows files from a platform with a lower
security level to be used. It is recommended to use this feature only in controlled
environments where the security implications are understood.
6 changes: 6 additions & 0 deletions Documentation/devel/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -1314,6 +1314,8 @@ grows with time, as Gramine adds functionality required by real-world workloads.
- ☑ `/dev/attestation/keys/<key_name>` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/_sgx_mrenclave` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/_sgx_mrsigner` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` <sup>[25](#attestation)</sup>
- ☑ `/dev/attestation/cpu_svn` <sup>[25](#attestation)</sup>
- ☑ `/dev/null` <sup>[23](#misc)</sup>
- ☑ `/dev/zero` <sup>[23](#misc)</sup>
- ☑ `/dev/random` <sup>[21](#randomness)</sup>
Expand Down Expand Up @@ -3245,6 +3247,10 @@ Gramine <low-level-dev-attestation-interface>`.
- ☑ `/dev/attestation/keys/<key_name>`
- ☑ `/dev/attestation/keys/_sgx_mrenclave` (only for SGX)
- ☑ `/dev/attestation/keys/_sgx_mrsigner` (only for SGX)
- ☑ `/dev/attestation/keys/svn/_sgx_mrenclave/<cpu_svn>` (only for SGX)

- ☑ `/dev/attestation/cpu_svn` (only for SGX)


</details><br />

Expand Down
10 changes: 9 additions & 1 deletion Documentation/manifest-syntax.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1088,7 +1088,7 @@ Encrypted files
::

fs.mounts = [
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false] },
{ type = "encrypted", path = "[PATH]", uri = "[URI]", key_name = "[KEY_NAME]", enable_recovery = [true|false], allow_tcb_migration = [true|false] },
]

fs.insecure__keys.[KEY_NAME] = "[32-character hex value]"
Expand Down Expand Up @@ -1160,6 +1160,14 @@ or disabling of recovery for different mounted files or directories. Note that
enabling this feature can negatively impact performance, as it writes to a
second shadow file for later recovery purposes on each flush.

The ``allow_tcb_migration`` mount parameter (default: ``false``) determines whether
the TCB migration feature is enabled for the mount. This feature allows sealed files to
be migrated to latest CPU SVN version after applying microcode updates. This feature
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting issue: There are two spaces between sentences instead of one. Should be "microcode updates. This feature" with only one space after the period.

Suggested change
be migrated to latest CPU SVN version after applying microcode updates. This feature
be migrated to latest CPU SVN version after applying microcode updates. This feature

Copilot uses AI. Check for mistakes.
is only valid for ``key_name`` of ``"_sgx_mrenclave"``. Enabling this feature can
negatively impact security, as it allow the enclave to unseal files that were created
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar issue: "allow" should be "allows" to maintain subject-verb agreement. The sentence should read "as it allows the enclave to unseal files".

Suggested change
negatively impact security, as it allow the enclave to unseal files that were created
negatively impact security, as it allows the enclave to unseal files that were created

Copilot uses AI. Check for mistakes.
with an old potentially vulnerable CPU SVN version. It is the responsibility of the
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting issue: There are trailing spaces at the end of the line. These should be removed to conform to documentation standards.

Suggested change
with an old potentially vulnerable CPU SVN version. It is the responsibility of the
with an old potentially vulnerable CPU SVN version. It is the responsibility of the

Copilot uses AI. Check for mistakes.
app developer to verify the integrity of the sealed files if this feature is enabled.

.. _untrusted-shared-memory:

Untrusted shared memory
Expand Down
9 changes: 9 additions & 0 deletions Documentation/pal/host-abi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,15 @@ random bits, to obtain an attestation report and quote, etc.
.. doxygenfunction:: PalGetSpecialKey
:project: pal

.. doxygenfunction:: PalGetSpecialKeyForSVN
:project: pal

Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting issue: There are trailing spaces at the end of the line. These should be removed to conform to documentation standards.

Copilot uses AI. Check for mistakes.
.. doxygenfunction:: PalGetCPUSVN
:project: pal

Comment on lines +369 to +372
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting issue: There are trailing spaces at the end of the line. These should be removed to conform to documentation standards.

Suggested change
.. doxygenfunction:: PalGetCPUSVN
:project: pal
.. doxygenfunction:: PalGetCPUSVN
:project: pal

Copilot uses AI. Check for mistakes.
.. doxygenfunction:: PalSetCPUSVN
:project: pal

.. doxygenfunction:: PalDeviceMap
:project: pal

Expand Down
4 changes: 4 additions & 0 deletions libos/include/libos_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ struct libos_mount_params {
/* Whether to enable file recovery (used by `chroot_encrypted` filesystem), false if not
* applicable */
bool enable_recovery;

/* Whether to allow TCB migration (used by `chroot_encrypted` filesystem), false if not
* applicable */
bool allow_tcb_migration;
};

struct libos_fs_ops {
Expand Down
28 changes: 28 additions & 0 deletions libos/include/libos_fs_encrypted.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@

#define RECOVERY_FILE_URI_SUFFIX ".gramine.recovery"

#define TCB_INFO_PERM_RW PERM_rw_rw_r__

#define TCB_INFO_FILE_NAME "gramine.tcb_info"

#define CPU_SVN_SIZE 16

#define OLD_TCB_FILE_URI_SUFFIX ".old_tcb"

typedef uint8_t cpu_svn_t[CPU_SVN_SIZE];

/*
* Represents a named key for opening files. The key might not be set yet: value of a key can be
* specified in the manifest, or set using `update_encrypted_files_key`. Before the key is set,
Expand Down Expand Up @@ -68,6 +78,13 @@ struct libos_encrypted_file {
*/
int init_encrypted_files(void);

/*
* \brief sets the default CPU SVN used for encrypted file keys.
*
* This is only used for testing.
*/
int set_cpu_svn(const cpu_svn_t* cpu_svn);

/*
* \brief Retrieve a key.
*
Expand Down Expand Up @@ -95,6 +112,15 @@ int list_encrypted_files_keys(int (*callback)(struct libos_encrypted_files_key*
*/
int get_or_create_encrypted_files_key(const char* name, struct libos_encrypted_files_key** out_key);

/*
* \brief Retrieve a key with a CPU SVN.
*
* Sets `*out_key` to a key with given name and CPU SVN. Creates a new key.
*
*/
int create_encrypted_files_key_for_svn(const char* name, cpu_svn_t* cpu_svn,
struct libos_encrypted_files_key** out_key);

/*
* \brief Read value of given key.
*
Expand Down Expand Up @@ -183,3 +209,5 @@ int encrypted_file_get_size(struct libos_encrypted_file* enc, file_off_t* out_si
int encrypted_file_set_size(struct libos_encrypted_file* enc, file_off_t size);

int parse_pf_key(const char* key_str, pf_key_t* pf_key);

int handle_tcb_migration(const char* uri, const char* key_name);
24 changes: 21 additions & 3 deletions libos/src/fs/chroot/encrypted.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "libos_fs.h"
#include "libos_fs_encrypted.h"
#include "libos_vma.h"
#include "pal.h"
#include "perm.h"
#include "stat.h"
#include "toml_utils.h"
Expand All @@ -60,16 +61,33 @@ static int chroot_encrypted_mount(struct libos_mount_params* params, void** moun
log_error("'%s' is invalid file URI", params->uri);
return -EINVAL;
}

int ret;
const char* key_name = params->key_name ?: "default";

if (params->allow_tcb_migration) {
if (!strcmp(key_name, PAL_KEY_NAME_SGX_MRENCLAVE)) {
log_warning("TCB migration will be supported for %s", params->uri);
ret = handle_tcb_migration(params->uri, key_name);
if (ret < 0) {
log_error("TCB migration failed for %s", params->uri);
return ret;
}

} else {
log_warning(
"TCB migration is only supported for keys named %s, ignoring "
"allow_tcb_migration for %s",
PAL_KEY_NAME_SGX_MRENCLAVE, params->uri);
}
}
struct libos_encrypted_files_key* key;
int ret = get_or_create_encrypted_files_key(key_name, &key);
ret = get_or_create_encrypted_files_key(key_name, &key);
if (ret < 0)
return ret;

*mount_data = key;
return 0;
return ret;

}

static ssize_t chroot_encrypted_checkpoint(void** checkpoint, void* mount_data) {
Expand Down
131 changes: 131 additions & 0 deletions libos/src/fs/dev/attestation.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/

#include "api.h"
#include "hex.h"
#include "libos_fs_encrypted.h"
#include "libos_fs_pseudo.h"
#include "pal.h"
Expand Down Expand Up @@ -84,6 +85,21 @@ static int user_report_data_save(struct libos_dentry* dent, const char* data, si
return 0;
}

#ifdef DEBUG
static int cpu_svn_save(struct libos_dentry* dent, const char* data, size_t size) {
log_debug("cpu_svn_save: saving %zu bytes", size);
__UNUSED(dent);
cpu_svn_t cpu_svn;
if (size != sizeof(cpu_svn)) {
log_warning("CPU SVN must be exactly %zu bytes, got %zu", sizeof(cpu_svn), size);
return -EINVAL;
}
memcpy(&cpu_svn, data, sizeof(cpu_svn));

return set_cpu_svn(&cpu_svn);
}
#endif /* DEBUG */

/*!
* \brief Modify target info used in `report` pseudo-file.
*
Expand Down Expand Up @@ -239,6 +255,32 @@ static int quote_load(struct libos_dentry* dent, char** out_data, size_t* out_si
return 0;
}

/*!
* \brief Get CPU SVN of the platform.
*/
static int cpu_svn_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
__UNUSED(dent);

cpu_svn_t cpu_svn;
size_t cpu_svn_size = sizeof(cpu_svn);
int ret = PalGetCPUSVN(&cpu_svn, &cpu_svn_size);
if (ret < 0) {
log_warning("PalGetCPUSVN failed: %s", pal_strerror(ret));
return pal_to_unix_errno(ret);
}

char* str = calloc(1, cpu_svn_size);

if (!str)
return -ENOMEM;

memcpy(str, &cpu_svn, sizeof(cpu_svn));

*out_data = str;
*out_size = cpu_svn_size;
return 0;
}

/*!
* \brief Get remote attestation type used.
*
Expand All @@ -264,6 +306,22 @@ static bool key_name_exists(struct libos_dentry* parent, const char* name) {
return key != NULL;
}

static bool key_name_exists_svn(struct libos_dentry* parent, const char* name) {
__UNUSED(parent);
if (strlen(name) != 2 * sizeof(cpu_svn_t)) {
log_warning("key_name_exists_svn: invalid key name length %zu of %s, expected %zu",
strlen(name), name, 2 * sizeof(cpu_svn_t));
return false;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)name, strlen(name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
}

return true;
}

struct key_list_names_data {
readdir_callback_t callback;
void* arg;
Expand All @@ -284,6 +342,13 @@ static int key_list_names(struct libos_dentry* parent, readdir_callback_t callba
return list_encrypted_files_keys(&key_list_names_callback, &data);
}

static int key_list_names_svn(struct libos_dentry* parent, readdir_callback_t callback, void* arg) {
__UNUSED(parent);
__UNUSED(callback);
__UNUSED(arg);
return 0;
}

static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size) {
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
if (!key)
Expand All @@ -307,6 +372,58 @@ static int key_load(struct libos_dentry* dent, char** out_data, size_t* out_size
return 0;
}

static int key_load_svn(struct libos_dentry* dent, char** out_data, size_t* out_size) {
if (strlen(dent->name) != 2 * sizeof(cpu_svn_t)) {
log_warning("key_name_exists_svn: invalid key name length");
return false;
}
cpu_svn_t cpu_svn;

if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
Comment on lines +378 to +384
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns false (boolean) but the return type is int. The function should return an appropriate integer error code such as -EINVAL instead of false.

Suggested change
return false;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
return -EINVAL;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return -EINVAL;

Copilot uses AI. Check for mistakes.
Comment on lines +378 to +384
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function returns false (boolean) but the return type is int. The function should return an appropriate integer error code such as -EINVAL instead of false.

Suggested change
return false;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return false;
return -EINVAL;
}
cpu_svn_t cpu_svn;
if (!hex2bytes((char*)dent->name, strlen(dent->name), &cpu_svn, sizeof(cpu_svn_t))) {
log_warning("key_name_exists_svn: invalid key name format");
return -EINVAL;

Copilot uses AI. Check for mistakes.
}

int ret;

char * key_name = dent->parent->name;
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after asterisk in pointer declaration. Should be char* key_name = dent->parent->name; instead of char * key_name = dent->parent->name; to be consistent with the codebase style.

Suggested change
char * key_name = dent->parent->name;
char* key_name = dent->parent->name;

Copilot uses AI. Check for mistakes.
struct libos_encrypted_files_key* key = NULL;
key = calloc(1, sizeof(*key));
if (!key) {
log_error("Cannot allocate memory for key");
ret = -ENOMEM;
goto out;
}
Comment on lines +391 to +396
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory allocation redundancy: The key is allocated on line 391, but then the pointer is overwritten by create_encrypted_files_key_for_svn on line 397. This first allocation is unnecessary and causes a memory leak. Remove the initial calloc since create_encrypted_files_key_for_svn creates and returns a new key.

Suggested change
key = calloc(1, sizeof(*key));
if (!key) {
log_error("Cannot allocate memory for key");
ret = -ENOMEM;
goto out;
}

Copilot uses AI. Check for mistakes.
ret = create_encrypted_files_key_for_svn(key_name, &cpu_svn, &key);
if (ret < 0) {
log_error("Cannot create or get key for SVN");
goto out;
}
pf_key_t pf_key;
bool is_set = read_encrypted_files_key(key, &pf_key);

if (is_set) {
char* buf = malloc(sizeof(pf_key));
if (!buf)
return -ENOMEM;
Comment on lines +407 to +408
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Memory leak: if malloc on line 406 fails, the function returns without freeing the previously allocated key structure and its key->name field. The error path cleanup should be reached via goto out instead of an early return.

Suggested change
if (!buf)
return -ENOMEM;
if (!buf) {
ret = -ENOMEM;
goto out;
}

Copilot uses AI. Check for mistakes.
memcpy(buf, &pf_key, sizeof(pf_key));

*out_data = buf;
*out_size = sizeof(pf_key);
} else {
*out_data = NULL;
*out_size = 0;
}
ret = 0;
out:
if (key) {
if (key->name)
free(key->name);
free(key);
}
return ret;
}

static int key_save(struct libos_dentry* dent, const char* data, size_t size) {
struct libos_encrypted_files_key* key = get_encrypted_files_key(dent->name);
if (!key)
Expand Down Expand Up @@ -338,6 +455,13 @@ static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_n
pseudo_add_str(attestation, "attestation_type", attestation_type_load);
pseudo_add_str(attestation, "my_target_info", &my_target_info_load);
pseudo_add_str(attestation, "report", &report_load);
struct pseudo_node* cpu_svn = pseudo_add_str(attestation, "cpu_svn", &cpu_svn_load);
#ifdef DEBUG
cpu_svn->perm = PSEUDO_PERM_FILE_RW;
cpu_svn->str.save = &cpu_svn_save;
#else
cpu_svn->perm = PSEUDO_PERM_FILE_R;
#endif /* DEBUG */

struct pseudo_node* user_report_data = pseudo_add_str(attestation, "user_report_data", NULL);
user_report_data->perm = PSEUDO_PERM_FILE_RW;
Expand All @@ -362,6 +486,13 @@ static int init_sgx_attestation(struct pseudo_node* attestation, struct pseudo_n
pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRENCLAVE, &key_load);
pseudo_add_str(keys, PAL_KEY_NAME_SGX_MRSIGNER, &key_load);

struct pseudo_node* keys_svn = pseudo_add_dir(keys, "svn");
struct pseudo_node* keys_svn_mrenclave_key =
pseudo_add_dir(keys_svn, PAL_KEY_NAME_SGX_MRENCLAVE);
struct pseudo_node* key_cpu_svn = pseudo_add_str(keys_svn_mrenclave_key, NULL, &key_load_svn);
key_cpu_svn->name_exists = &key_name_exists_svn;
key_cpu_svn->list_names = &key_list_names_svn;

if (!strcmp(g_pal_public_state->attestation_type, "none")) {
log_debug("host is Linux-SGX and remote attestation type is 'none', skipping "
"/dev/attestation/quote file");
Expand Down
Loading