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

stefanberger / libtpms / #2034

04 Dec 2025 02:36PM UTC coverage: 77.211% (-0.007%) from 77.218%
#2034

push

travis-ci

web-flow
Merge b188c9a86 into 4f71e9b45

77 of 91 new or added lines in 17 files covered. (84.62%)

902 existing lines in 26 files now uncovered.

36127 of 46790 relevant lines covered (77.21%)

125165.78 hits per line

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

92.31
/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: As of February, 2016, the failure processing has been moved to the
54
// platform-specific code. When the TPM code encounters an unrecoverable failure, it
55
// will SET g_inFailureMode and call _plat__Fail(). That function should not return
56
// but may call ExecuteCommand().
57
//
58
LIB_EXPORT void ExecuteCommand(
17,790✔
59
    uint32_t        requestSize,   // IN: command buffer size
60
    unsigned char*  request,       // IN: command buffer
61
    uint32_t*       responseSize,  // IN/OUT: response buffer size
62
    unsigned char** response       // IN/OUT: response buffer
63
)
64
{
65
    // Command local variables
66
    UINT32  commandSize;
17,790✔
67
    COMMAND command;
17,790✔
68

69
    // Response local variables
70
    UINT32 maxResponse = *responseSize;
17,790✔
71
    TPM_RC result;  // return code for the command
17,790✔
72

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

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

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

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

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

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

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

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

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

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

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

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

276
Cleanup:
14,586✔
277
    if(g_clearOrderly == TRUE && NV_IS_ORDERLY)
17,790✔
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,790✔
291

292
    // The parameters and sessions have been marshaled. Now tack on the header and
293
    // set the sizes
294
    BuildResponseHeader(&command, *response, result);
17,790✔
295

296
    // Try to commit all the writes to NV if any NV write happened during this
297
    // command execution. This check should be made for both succeeded and failed
298
    // commands, because a failed one may trigger a NV write in DA logic as well.
299
    // This is the only place in the command execution path that may call the NV
300
    // commit. If the NV commit fails, the TPM should be put in failure mode.
301
    if((g_updateNV != UT_NONE) && !g_inFailureMode)
17,790✔
302
    {
303
        if(g_updateNV == UT_ORDERLY)
6,792✔
304
            NvUpdateIndexOrderlyData();
15✔
305
        if(!NvCommit())
6,792✔
UNCOV
306
            FAIL(FATAL_ERROR_INTERNAL);
×
307
        g_updateNV = UT_NONE;
6,792✔
308
    }
309
    pAssert((UINT32)command.parameterSize <= maxResponse);
17,790✔
310

311
    // Clear unused bits in response buffer.
312
    MemorySet(*response + *responseSize, 0, maxResponse - *responseSize);
17,790✔
313

314
    // as a final act, and not before, update the response size.
315
    *responseSize = (UINT32)command.parameterSize;
17,790✔
316

317
    return;
17,790✔
318
}
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