• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stefanberger / libtpms / #2036

05 Dec 2025 02:40PM UTC coverage: 77.213% (-0.01%) from 77.226%
#2036

push

travis-ci

web-flow
Merge fef9f3885 into 4f71e9b45

870 of 999 new or added lines in 77 files covered. (87.09%)

950 existing lines in 29 files now uncovered.

36283 of 46991 relevant lines covered (77.21%)

126719.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.4
/src/tpm2/ExecCommand.c
1
// SPDX-License-Identifier: BSD-2-Clause
2

3
//** Introduction
4
//
5
// This file contains the entry function ExecuteCommand() which provides the main
6
// control flow for TPM command execution.
7

8
//** Includes
9

10
#include "Tpm.h"
11
#include "Marshal.h"
12
// TODO_RENAME_INC_FOLDER:platform_interface refers to the TPM_CoreLib platform interface
13
#include "ExecCommand_fp.h"        // libtpms changed
14

15
// Uncomment this next #include if doing static command/response buffer sizing
16
// #include "CommandResponseSizes_fp.h"
17

18
#define TPM_HAVE_TPM2_DECLARATIONS
19
#include "tpm_library_intern.h"  // libtpms added
20

21
//** ExecuteCommand()
22
//
23
// The function performs the following steps.
24
//
25
//  a)  Parses the command header from input buffer.
26
//  b)  Calls ParseHandleBuffer() to parse the handle area of the command.
27
//  c)  Validates that each of the handles references a loaded entity.
28
//  d)  Calls ParseSessionBuffer () to:
29
//      1)  unmarshal and parse the session area;
30
//      2)  check the authorizations; and
31
//      3)  when necessary, decrypt a parameter.
32
//  e)  Calls CommandDispatcher() to:
33
//      1)  unmarshal the command parameters from the command buffer;
34
//      2)  call the routine that performs the command actions; and
35
//      3)  marshal the responses into the response buffer.
36
//  f)  If any error occurs in any of the steps above create the error response
37
//      and return.
38
//  g)  Calls BuildResponseSession() to:
39
//      1)  when necessary, encrypt a parameter
40
//      2)  build the response authorization sessions
41
//      3)  update the audit sessions and nonces
42
//  h)  Calls BuildResponseHeader() to complete the construction of the response.
43
//
44
// 'responseSize' is set by the caller to the maximum number of bytes available in
45
// the output buffer. ExecuteCommand will adjust the value and return the number
46
// of bytes placed in the buffer.
47
//
48
// 'response' is also set by the caller to indicate the buffer into which
49
//  ExecuteCommand is to place the response.
50
//
51
//  'request' and 'response' may point to the same buffer
52
//
53
// Note: The failure processing has been moved to the
54
// platform-specific code. When the TPM code encounters an unrecoverable failure, it
55
// will call _plat__Fail() and call _plat__InFailureMode() to query failure mode.
56
LIB_EXPORT void ExecuteCommand(
17,796✔
57
    uint32_t        requestSize,   // IN: command buffer size
58
    unsigned char*  request,       // IN: command buffer
59
    uint32_t*       responseSize,  // IN/OUT: response buffer size
60
    unsigned char** response       // IN/OUT: response buffer
61
)
62
{
63
    // Command local variables
64
    UINT32  commandSize;
17,796✔
65
    COMMAND command;
17,796✔
66

67
    // Response local variables
68
    UINT32 maxResponse = *responseSize;
17,796✔
69
    TPM_RC result;  // return code for the command
17,796✔
70

71
    /* check for an unreasonably large command size, since it's cast to a signed integer later */
72
    if (requestSize > INT32_MAX) {
17,796✔
UNCOV
73
        result = TPM_RC_SUCCESS;
×
UNCOV
74
        goto Cleanup;
×
75
    }
76
    // This next function call is used in development to size the command and response
77
    // buffers. The values printed are the sizes of the internal structures and
78
    // not the sizes of the canonical forms of the command response structures. Also,
79
    // the sizes do not include the tag, command.code, requestSize, or the authorization
80
    // fields.
81
    //CommandResponseSizes();
82

83
    // Set flags for NV access state. This should happen before any other
84
    // operation that may require a NV write. Note, that this needs to be done
85
    // even when in failure mode. Otherwise, g_updateNV would stay SET while in
86
    // Failure mode and the NV would be written on each call.
87
    g_updateNV     = UT_NONE;
17,796✔
88
    g_clearOrderly = FALSE;
17,796✔
89

90
    if(!g_initCompleted)
17,796✔
91
    {
92
        // no return because failure will happen immediately below. this is
93
        // treated as fatal because it is a system level failure for there to be
94
        // no TPM_INIT indication.  Since init is an out-of-band indication from
95
        // Execute command, we don't return TPM_RC_INITIALIZE which refers to
96
        // the TPM2_Startup command
NEW
97
        FAIL_NORET(FATAL_ERROR_NO_INIT);
×
98
    }
99

100
    if(_plat__InFailureMode())
17,796✔
101
    {
102
        // Do failure mode processing
UNCOV
103
        TpmFailureMode(requestSize, request, responseSize, response);
×
UNCOV
104
        return;
×
105
    }
106
    // Query platform to get the NV state.  The result state is saved internally
107
    // and will be reported by NvIsAvailable(). The reference code requires that
108
    // accessibility of NV does not change during the execution of a command.
109
    // Specifically, if NV is available when the command execution starts and then
110
    // is not available later when it is necessary to write to NV, then the TPM
111
    // will go into failure mode.
112
    NvCheckState();
17,796✔
113

114
    // Due to the limitations of the simulation, TPM clock must be explicitly
115
    // synchronized with the system clock whenever a command is received.
116
    // This function call is not necessary in a hardware TPM. However, taking
117
    // a snapshot of the hardware timer at the beginning of the command allows
118
    // the time value to be consistent for the duration of the command execution.
119
    TimeUpdateToCurrent();
17,796✔
120

121
    // Any command through this function will unceremoniously end the
122
    // _TPM_Hash_Data/_TPM_Hash_End sequence.
123
    if(g_DRTMHandle != TPM_RH_UNASSIGNED)
17,796✔
UNCOV
124
        ObjectTerminateEvent();
×
125

126
    // Get command buffer size and command buffer.
127
    command.tag = 0;                                // libtpms added: Coverity
17,796✔
128
    command.parameterBuffer = request;
17,796✔
129
    command.parameterSize   = requestSize;
17,796✔
130

131
    // Parse command header: tag, commandSize and command.code.
132
    // First parse the tag. The unmarshaling routine will validate
133
    // that it is either TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS.
134
    result = TPMI_ST_COMMAND_TAG_Unmarshal(
17,796✔
135
        &command.tag, &command.parameterBuffer, &command.parameterSize);
136
    if(result != TPM_RC_SUCCESS)
17,796✔
137
        goto Cleanup;
12✔
138
    // Unmarshal the commandSize indicator.
139
    result = UINT32_Unmarshal(
17,784✔
140
        &commandSize, &command.parameterBuffer, &command.parameterSize);
141
    if(result != TPM_RC_SUCCESS)
17,784✔
142
        goto Cleanup;
4✔
143
    // On a TPM that receives bytes on a port, the number of bytes that were
144
    // received on that port is requestSize it must be identical to commandSize.
145
    // In addition, commandSize must not be larger than MAX_COMMAND_SIZE allowed
146
    // by the implementation. The check against MAX_COMMAND_SIZE may be redundant
147
    // as the input processing (the function that receives the command bytes and
148
    // places them in the input buffer) would likely have the input truncated when
149
    // it reaches MAX_COMMAND_SIZE, and requestSize would not equal commandSize.
150
    if(commandSize != requestSize || commandSize > MAX_COMMAND_SIZE)
17,780✔
151
    {
152
        result = TPM_RC_COMMAND_SIZE;
4✔
153
        goto Cleanup;
4✔
154
    }
155
    // Unmarshal the command code.
156
    result = TPM_CC_Unmarshal(
17,776✔
157
        &command.code, &command.parameterBuffer, &command.parameterSize);
158
    if(result != TPM_RC_SUCCESS)
17,776✔
159
        goto Cleanup;
4✔
160
    // Check to see if the command is implemented.
161
    command.index = CommandCodeToCommandIndex(command.code);
17,772✔
162
    if(UNIMPLEMENTED_COMMAND_INDEX == command.index)
17,772✔
163
    {
164
        result = TPM_RC_COMMAND_CODE;
5✔
165
        goto Cleanup;
5✔
166
    }
167
#if CC_ReadOnlyControl
168
    // Check if the TPM is operating in Read-Only mode. If so, reject commands
169
    // that are disallowed in this mode before performing any further auth checks.
170
    // The execution of some commands may still be disallowed under certain conditions,
171
    // but those will be evaluated in the corresponding command implementation.
172
    if(gc.readOnly && IsDisallowedInReadOnlyMode(command.index))
173
    {
174
        result = TPM_RC_READ_ONLY;
175
        goto Cleanup;
176
    }
177
#endif
178
#if FIELD_UPGRADE_IMPLEMENTED == YES
179
    // If the TPM is in FUM, then the only allowed command is
180
    // TPM_CC_FieldUpgradeData.
181
    if(IsFieldUgradeMode() && (command.code != TPM_CC_FieldUpgradeData))
182
    {
183
        result = TPM_RC_UPGRADE;
184
        goto Cleanup;
185
    }
186
    else
187
#endif
188
        // Excepting FUM, the TPM only accepts TPM2_Startup() after
189
        // _TPM_Init. After getting a TPM2_Startup(), TPM2_Startup()
190
        // is no longer allowed.
191
        if((!TPMIsStarted() && command.code != TPM_CC_Startup)
17,767✔
192
           || (TPMIsStarted() && command.code == TPM_CC_Startup))
17,742✔
193
        {
194
            result = TPM_RC_INITIALIZE;
36✔
195
            goto Cleanup;
36✔
196
        }
197
    // Start regular command process.
198
    NvIndexCacheInit();
17,731✔
199
    // Parse Handle buffer.
200
    result = ParseHandleBuffer(&command);
17,731✔
201
    if(result != TPM_RC_SUCCESS)
17,731✔
202
        goto Cleanup;
514✔
203
    // All handles in the handle area are required to reference TPM-resident
204
    // entities.
205
    result = EntityGetLoadStatus(&command);
17,217✔
206
    if(result != TPM_RC_SUCCESS)
17,217✔
207
        goto Cleanup;
370✔
208
    // Authorization session handling for the command.
209
    ClearCpRpHashes(&command);
16,847✔
210
    if(command.tag == TPM_ST_SESSIONS)
16,847✔
211
    {
212
        // Find out session buffer size.
213
        result = UINT32_Unmarshal((UINT32*)&command.authSize,
6,336✔
214
                                  &command.parameterBuffer,
215
                                  &command.parameterSize);
216
        if(result != TPM_RC_SUCCESS)
6,336✔
217
            goto Cleanup;
4✔
218
        // Perform sanity check on the unmarshaled value. If it is smaller than
219
        // the smallest possible session or larger than the remaining size of
220
        // the command, then it is an error. NOTE: This check could pass but the
221
        // session size could still be wrong. That will be determined after the
222
        // sessions are unmarshaled.
223
        if(command.authSize < 9 || command.authSize > command.parameterSize)
6,332✔
224
        {
225
            result = TPM_RC_SIZE;
24✔
226
            goto Cleanup;
24✔
227
        }
228
        command.parameterSize -= command.authSize;
6,308✔
229

230
        // The actions of ParseSessionBuffer() are described in the introduction.
231
        // As the sessions are parsed command.parameterBuffer is advanced so, on a
232
        // successful return, command.parameterBuffer should be pointing at the
233
        // first byte of the parameters.
234
        result = ParseSessionBuffer(&command);
6,308✔
235
        if(result != TPM_RC_SUCCESS)
6,308✔
236
            goto Cleanup;
273✔
237
    }
238
    else
239
    {
240
        command.authSize = 0;
10,511✔
241
        // The command has no authorization sessions.
242
        // If the command requires authorizations, then CheckAuthNoSession() will
243
        // return an error.
244
        result = CheckAuthNoSession(&command);
10,511✔
245
        if(result != TPM_RC_SUCCESS)
10,511✔
246
            goto Cleanup;
24✔
247
    }
248
    // Set up the response buffer pointers. CommandDispatch will marshal the
249
    // response parameters starting at the address in command.responseBuffer.
250
    //*response = MemoryGetResponseBuffer(command.index);
251
    // leave space for the command header
252
    command.responseBuffer = *response + STD_RESPONSE_HEADER;
16,522✔
253

254
    // leave space for the parameter size field if needed
255
    if(command.tag == TPM_ST_SESSIONS)
16,522✔
256
        command.responseBuffer += sizeof(UINT32);
6,035✔
257
    if(IsHandleInResponse(command.index))
16,522✔
258
        command.responseBuffer += sizeof(TPM_HANDLE);
2,633✔
259

260
    // CommandDispatcher returns a response handle buffer and a response parameter
261
    // buffer if it succeeds. It will also set the parameterSize field in the
262
    // buffer if the tag is TPM_RC_SESSIONS.
263
    result = CommandDispatcher(&command);
16,522✔
264
    if(result != TPM_RC_SUCCESS)
16,522✔
265
        goto Cleanup;
1,930✔
266

267
    // Build the session area at the end of the parameter area.
268
    result = BuildResponseSession(&command);
14,592✔
269
    if(result != TPM_RC_SUCCESS)
14,592✔
270
    {
271
        goto Cleanup;
272
    }
273

274
Cleanup:
14,592✔
275
    if(!_plat__InFailureMode())
17,796✔
276
    {
277
        if(g_clearOrderly == TRUE && NV_IS_ORDERLY)
17,796✔
278
        {
279
#if USE_DA_USED
280
            gp.orderlyState = g_daUsed ? SU_DA_USED_VALUE : SU_NONE_VALUE;
2✔
281
#else
282
            gp.orderlyState = SU_NONE_VALUE;
283
#endif
284
            NV_SYNC_PERSISTENT(orderlyState);
2✔
285
        }
286
        // This implementation loads an "evict" object to a transient object slot in
287
        // RAM whenever an "evict" object handle is used in a command so that the
288
        // access to any object is the same. These temporary objects need to be
289
        // cleared from RAM whether the command succeeds or fails.
290
        ObjectCleanupEvict();
17,796✔
291

292
        // The parameters and sessions have been marshaled. Now tack on the header and
293
        // set the sizes.  This sets command.parameterSize to the size of the entire
294
        // response.
295
        BuildResponseHeader(&command, *response, result);
17,796✔
296

297
        // Try to commit all the writes to NV if any NV write happened during this
298
        // command execution. This check should be made for both succeeded and failed
299
        // commands, because a failed one may trigger a NV write in DA logic as well.
300
        // This is the only place in the command execution path that may call the NV
301
        // commit. If the NV commit fails, the TPM should be put in failure mode.
302
        // Don't write in failure mode because we can't trust what we are
303
        // writing.
304
        if((g_updateNV != UT_NONE) && !_plat__InFailureMode())
17,796✔
305
        {
306
            if(g_updateNV == UT_ORDERLY)
6,795✔
307
            {
308
                NvUpdateIndexOrderlyData();
15✔
309
            }
310
            if(!NvCommit())
6,795✔
311
            {
NEW
312
                FAIL_NORET(FATAL_ERROR_INTERNAL);
×
313
            }
314
            g_updateNV = UT_NONE;
6,795✔
315
        }
316

317
        pAssert_NORET((UINT32)command.parameterSize <= maxResponse);
17,796✔
318

319
        // Clear unused bits in response buffer.
320
        MemorySet(*response + *responseSize, 0, maxResponse - *responseSize);
17,796✔
321

322
        // as a final act, and not before, update the response size.
323
        *responseSize = (UINT32)command.parameterSize;
17,796✔
324
    }
325

326
    if(_plat__InFailureMode())
17,796✔
327
    {
328
        // something in the command triggered failure mode - handle command as a failure instead
NEW
329
        TpmFailureMode(requestSize, request, responseSize, response);
×
330
    }
331
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc