intel / intel/confidential-computing.tdx.tdx-module

Incorrect output operand values on TD Call failure due to invalid RAX (invalid version/reserved bits)

Open
#20 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
No language data
Stars
113
Forks
24
PR merge metrics
No merged PRs in 30d

Description

### Summary
I would like to report a class of issues affecting TD calls, resulting in incorrect output operand values when some TD calls fail due to one of the following reasons.
**Case1:** TD/SEAM call Version Number is incorrect—invalid value in input operand: RAX[16:23].
**Case2:** Reserved bits in the input operand:RAX[24:63] are not zero.
In these cases, the TD call fails with return code 0xC000010000000000, TDX_OPERAND_INVALID-OPERAND_ID_RAX.

### Details
Some of the output operand values are not properly set by the TDX Module to the expected values before the TD call returns due to the above two failure **Cases**. It is noted that the same output operand values are correctly set as per the ABI specification for other failure cases.

While the following may not be the most rigorous classification, I have grouped these issues based on the similarity of output operand format descriptions in the TDX API specification to keep the report concise.

|Group#| Affected TD call/s |Expected Output Operand/s Definition as per the ABI spec | Current behavior | Comments |
|-| ------------- | ------------| ----------------- | ----|
| 1|TDG.VM.WR, TDG.VP.WR, TDG.SERVTD.WR | R8: Previous contents of the field. In case of an error, as indicated by RAX, R8 returns 0.| The Input R8 value remains unchanged. | |
| 2|TDG.VP.INFO | RAX: returns a constant value of TDX_SUCCESS (0). | API call does not always succeed with constant RAX=0. Sometimes fails and returns RAX=0xC000010000000000 | |
| 3| TDG.SYS.RDALL | R8: In case of an error, as indicated by RAX, R8 returns -1. | Input R8 value remains unchanged. | |
| 4| TDG.VM.RD | RDX: For TDG.VM.RD version 0, RDX is unmodified. For TDG.VM.RD version 1 or higher: • If the input field identifier was -1, RDX returns the first readable field identifier. • **Else, in case of an error, RDX returns -1**. On success, RDX returns the next readable field identifier. A value of -1 indicates no next field identifier is available. | Input RDX value remains unchanged. | For **Case1**, the RDX value is correct(set to -1 as expected). For **Case2**: The RDX is incorrect(remains unchanged). |
| 5| TDG.VP.RD, TDG.SYS.RD, TDG.SERVTD.RD | RDX: If the input field identifier was -1, RDX returns the first readable field identifier. **Else, in case of an error, RDX returns -1**. On success, RDX returns the next readable field identifier. A value of -1 indicates no next field identifier is available. | Input RDX value remains unchanged. | |

I have tested many of the above cases, but not all.

In above, I have only considered scenarios where the output operand register also serves as an input operand to the TD call. i.e. The register is originally set by the TD prior to the call and is expected to be updated by the Module. However, there are other examples where the register of interest is not an input operand, and the output register does not comply with the ABI spec.
For example, In TDG.VM.RD, the R8 is not an input operand. However, according to the ABI spec, R8 is an output operand that should return the "Contents of the field" read. If the TD call fails, R8 must return 0. However in **Case2**, R8 is not set to 0 by the TDX Module.

### Analysis
I noticed that the TD call dispatcher validates the reserved bits in RAX and the TD call version. However, if those validations fail, the Module does not set the output operands as per the ABI spec prior to TD call returns.

void td_call(tdx_module_local_t* tdx_local_data_ptr, bool_t* interrupt_occurred)
{
...

if ((leaf_opcode.reserved0 != 0) || (leaf_opcode.reserved1 != 0))
{
TDX_ERROR("Leaf and version not supported 0x%llx\n", leaf_opcode.raw);
retval = api_error_with_operand_id(TDX_OPERAND_INVALID, OPERAND_ID_RAX);
goto EXIT;
}

// Only a few functions have multiple versions
if ((leaf_opcode.version > 0) &&
(leaf_opcode.leaf != TDG_VM_RD_LEAF))
{
TDX_ERROR("Invalid version %d for leaf %d\n", leaf_opcode.version, leaf_opcode.leaf);
retval = api_error_with_operand_id(TDX_OPERAND_INVALID, OPERAND_ID_RAX);
goto EXIT;
}

...

For TDG_VM_RD_LEAF, the version validation takes place in the TD call handler and therefore the output operand values (RAX, R8) are in fact correctly set by the handler function in failure **Case1**.

Appreciate it if you could help clarify/confirm these.

Thank you!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.