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

stefanberger / libtpms / #2037

05 Dec 2025 06:50PM UTC coverage: 77.185% (-0.03%) from 77.213%
#2037

push

travis-ci

web-flow
Merge 73eca8c26 into 4f71e9b45

992 of 1176 new or added lines in 85 files covered. (84.35%)

1748 existing lines in 62 files now uncovered.

36357 of 47104 relevant lines covered (77.18%)

125949.64 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
    // Taking a snapshot of the hardware timer at the beginning of the command
115
    // allows the time value to be consistent for the duration of the command
116
    // execution.  This will also update the NV time state if appropriate.
117
    TimeUpdateToCurrent();
17,796✔
118

119
    // Any command through this function will unceremoniously end the
120
    // _TPM_Hash_Data/_TPM_Hash_End sequence.
121
    if(g_DRTMHandle != TPM_RH_UNASSIGNED)
17,796✔
NEW
122
        ObjectTerminateDRTMEvent();
×
123

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

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

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

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

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

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

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

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

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

315
        pAssert_NORET((UINT32)command.parameterSize <= maxResponse);
17,796✔
316

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

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

324
    if(_plat__InFailureMode())
17,796✔
325
    {
326
        // something in the command triggered failure mode - handle command as a failure instead
NEW
327
        TpmFailureMode(requestSize, request, responseSize, response);
×
328
    }
329
}
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