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

stefanberger / swtpm / #2885

24 Feb 2026 07:01PM UTC coverage: 71.687% (-0.7%) from 72.417%
#2885

push

travis-ci

web-flow
Merge 4834fc88d into 48358b244

34 of 215 new or added lines in 8 files covered. (15.81%)

466 existing lines in 9 files now uncovered.

7530 of 10504 relevant lines covered (71.69%)

10227.55 hits per line

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

92.55
/src/swtpm/mainloop.c
1
/*
2
 * mainloop.c -- The TPM Emulator's main processing loop
3
 *
4
 * (c) Copyright IBM Corporation 2014, 2015, 2016
5
 *
6
 * Author: Stefan Berger <stefanb@us.ibm.com>
7
 *
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions are
12
 * met:
13
 *
14
 * Redistributions of source code must retain the above copyright notice,
15
 * this list of conditions and the following disclaimer.
16
 *
17
 * Redistributions in binary form must reproduce the above copyright
18
 * notice, this list of conditions and the following disclaimer in the
19
 * documentation and/or other materials provided with the distribution.
20
 *
21
 * Neither the names of the IBM Corporation nor the names of its
22
 * contributors may be used to endorse or promote products derived from
23
 * this software without specific prior written permission.
24
 *
25
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
 */
37

38
/* mainLoop() is the main server loop.
39

40
   It reads a TPM request, processes the ordinal, and writes the response
41
*/
42

43
#include <config.h>
44

45
#include <errno.h>
46
#include <stdio.h>
47
#include <stdint.h>
48
#include <stdbool.h>
49
#include <stdlib.h>
50
#include <string.h>
51
#include <poll.h>
52
#include <sys/stat.h>
53
#include <fcntl.h>
54
#include <sys/socket.h>
55

56
#include <libtpms/tpm_error.h>
57
#include <libtpms/tpm_library.h>
58
#include <libtpms/tpm_memory.h>
59

60
#include "swtpm_debug.h"
61
#include "swtpm_io.h"
62
#include "tpmlib.h"
63
#include "locality.h"
64
#include "logging.h"
65
#include "ctrlchannel.h"
66
#include "mainloop.h"
67
#include "utils.h"
68
#include "sys_dependencies.h"
69
#include "compiler_dependencies.h"
70
#include "swtpm_utils.h"
71
#include "swtpm_nvstore.h"
72

73
/* local variables */
74
static TPM_MODIFIER_INDICATOR g_locality;
75

76
bool g_mainloop_terminate;
77

78
TPM_RESULT
79
mainloop_cb_get_locality(TPM_MODIFIER_INDICATOR *loc,
28,531✔
80
                         uint32_t tpmnum SWTPM_ATTR_UNUSED)
81
{
82
    *loc = g_locality;
28,531✔
83

84
    return TPM_SUCCESS;
28,531✔
85
}
86

87
/* ensure that the storage is locked; returns false in case of failure */
88
bool mainloop_ensure_locked_storage(struct mainLoopParams *mlp)
42,570✔
89
{
90
    TPM_RESULT res;
42,570✔
91

92
    if (mlp->storage_locked)
42,570✔
93
        return true;
94

95
    /* if NVRAM hasn't been initialized yet locking may need to be retried */
96
    res = SWTPM_NVRAM_Lock_Storage(mlp->locking_retries);
155✔
97
    if (res == TPM_RETRY)
155✔
98
        return true;
99
    if (res != TPM_SUCCESS)
29✔
100
        return false;
101

102
    mlp->locking_retries = 0;
29✔
103
    mlp->storage_locked = true;
29✔
104
    mlp->incoming_migration = false;
29✔
105

106
    return true;
29✔
107
}
108

109
void mainloop_unlock_nvram(struct mainLoopParams *mlp,
7✔
110
                           unsigned int locking_retries)
111
{
112
    SWTPM_NVRAM_Unlock();
7✔
113

114
    mlp->storage_locked = false;
7✔
115
    mlp->locking_retries = locking_retries;
7✔
116
}
7✔
117

118
int mainLoop(struct mainLoopParams *mlp, int notify_fd, bool tpm_running)
423✔
119
{
120
    TPM_RESULT          rc = 0;
423✔
121
    TPM_CONNECTION_FD   connection_fd;             /* file descriptor for read/write */
423✔
122
    unsigned char       *command = NULL;           /* command buffer */
423✔
123
    uint32_t            command_length;            /* actual length of command bytes */
423✔
124
    uint32_t            max_command_length;        /* command buffer size */
423✔
125
    off_t               cmd_offset;
423✔
126
    /* The response buffer is reused for each command. Thus it can grow but never shrink */
127
    unsigned char       *rbuffer = NULL;           /* actual response bytes */
423✔
128
    uint32_t            rlength = 0;               /* bytes in response buffer */
423✔
129
    uint32_t            rTotal = 0;                /* total allocated bytes */
423✔
130
    int                 ctrlfd;
423✔
131
    int                 ctrlclntfd;
423✔
132
    int                 sockfd;
423✔
133
    int                 ready;
423✔
134
    struct iovec        iov[3];
423✔
135
    uint32_t            ack = htobe32(0);
423✔
136
    struct tpm2_resp_prefix respprefix;
423✔
137
    uint32_t            lastCommand;
423✔
138

139
    /* poolfd[] indexes */
140
    enum {
423✔
141
        DATA_CLIENT_FD = 0,
142
        NOTIFY_FD,
143
        CTRL_SERVER_FD,
144
        CTRL_CLIENT_FD,
145
        DATA_SERVER_FD
146
    };
147

148
    TPM_DEBUG("mainLoop:\n");
423✔
149

150
    max_command_length = tpmlib_get_tpm_property(TPMPROP_TPM_BUFFER_MAX) +
423✔
151
                         sizeof(struct tpm2_send_command_prefix);
152

153
    command = malloc(max_command_length);
423✔
154
    if (!command) {
423✔
155
        logprintf(STDERR_FILENO, "Could not allocate %u bytes for buffer.\n",
×
156
                  max_command_length);
157
        return TPM_FAIL;
×
158
    }
159

160
    /* header and trailer that we may send by setting iov_len */
161
    iov[0].iov_base = &respprefix;
423✔
162
    iov[0].iov_len = 0;
423✔
163
    iov[2].iov_base = &ack;
423✔
164
    iov[2].iov_len = 0;
423✔
165

166
    connection_fd.fd = -1;
423✔
167
    ctrlfd = ctrlchannel_get_fd(mlp->cc);
423✔
168
    ctrlclntfd = ctrlchannel_get_client_fd(mlp->cc);
423✔
169

170
    sockfd = SWTPM_IO_GetSocketFD();
423✔
171

172
    if (mlp->startupType != _TPM_ST_NONE) {
423✔
173
        command_length = tpmlib_create_startup_cmd(
213✔
174
                                  mlp->startupType,
175
                                  mlp->tpmversion,
176
                                  command, max_command_length);
177
        if (command_length > 0) {
213✔
178
            pcap_packet_record_write(&mlp->ps, command, command_length, true);
213✔
179

180
            mlp->lastCommand = tpmlib_get_cmd_ordinal(command, command_length);
213✔
181
            rc = TPMLIB_Process(&rbuffer, &rlength, &rTotal,
213✔
182
                                command, command_length);
183

184
            pcap_packet_record_write(&mlp->ps, rbuffer, rlength, true);
213✔
185
        }
186

187
        if (rc || command_length == 0) {
213✔
188
            g_mainloop_terminate = true;
×
189
            if (rc)
×
190
                logprintf(STDERR_FILENO, "Could not send Startup: 0x%x\n", rc);
×
191
        }
192
    }
193

194
    while (!g_mainloop_terminate) {
21,476✔
195

196
        while (rc == 0) {
142,705✔
197
            if (mlp->flags & MAIN_LOOP_FLAG_USE_FD) {
122,619✔
198
                if (connection_fd.fd != mlp->fd) {
1,480✔
199
                    SWTPM_IO_Disconnect(&connection_fd);
150✔
200
                    connection_fd.fd = mlp->fd;
150✔
201
                }
202
            }
203

204
            struct pollfd pollfds[] = {
122,619✔
205
                [DATA_CLIENT_FD] = {
206
                    .fd = connection_fd.fd,
122,619✔
207
                    .events = POLLIN | POLLHUP,
208
                    .revents = 0,
209
                },
210
                [NOTIFY_FD] = {
211
                    .fd = notify_fd,
212
                    .events = POLLIN,
213
                    .revents = 0,
214
                },
215
                [CTRL_SERVER_FD] = {
216
                    .fd = -1,
217
                    .events = POLLIN,
218
                    .revents = 0,
219
                },
220
                [CTRL_CLIENT_FD] = {
221
                    .fd = ctrlclntfd,
222
                    .events = POLLIN | POLLHUP,
223
                    .revents = 0,
224
                },
225
                [DATA_SERVER_FD] = {
226
                    /* listen socket for accepting clients */
227
                    .fd = -1,
228
                    .events = POLLIN,
229
                    .revents = 0,
230
                }
231
            };
232

233
            /* only listend for clients if we don't have one */
234
            if (connection_fd.fd < 0)
122,619✔
235
                pollfds[DATA_SERVER_FD].fd = sockfd;
79,740✔
236
            if (ctrlclntfd < 0)
122,619✔
237
                pollfds[CTRL_SERVER_FD].fd = ctrlfd;
80,783✔
238

239
            ready = poll(pollfds, 5, -1);
122,619✔
240
            if (ready < 0 && errno == EINTR)
122,619✔
241
                continue;
79,722✔
242

243
            if (ready < 0 ||
122,616✔
244
                (pollfds[NOTIFY_FD].revents & POLLIN) != 0) {
122,616✔
245
                SWTPM_IO_Disconnect(&connection_fd);
3✔
246
                break;
974✔
247
            }
248

249
            if (pollfds[DATA_CLIENT_FD].revents & (POLLHUP | POLLERR)) {
122,613✔
250
                logprintf(STDERR_FILENO, "Data client disconnected\n");
87✔
251
                mlp->fd = -1;
87✔
252
                /* chardev and unixio get this signal, not tcp */
253
                if (mlp->flags & MAIN_LOOP_FLAG_END_ON_HUP) {
87✔
254
                    /* only the chardev terminates here */
255
                    g_mainloop_terminate = true;
×
256
                    break;
×
257
                }
258
            }
259

260
            if (pollfds[DATA_SERVER_FD].revents & POLLIN)
122,613✔
261
                connection_fd.fd = accept(pollfds[DATA_SERVER_FD].fd, NULL, 0);
20,635✔
262

263
            if (pollfds[CTRL_SERVER_FD].revents & POLLIN)
122,613✔
264
                ctrlclntfd = accept(ctrlfd, NULL, 0);
18,813✔
265

266
            if (pollfds[CTRL_CLIENT_FD].revents & POLLIN) {
122,613✔
267
                ctrlclntfd = ctrlchannel_process_fd(ctrlclntfd,
40,753✔
268
                                                    &g_mainloop_terminate,
269
                                                    &g_locality, &tpm_running,
270
                                                    mlp);
271
                if (ctrlclntfd < 0 &&
40,753✔
272
                    mlp->flags & MAIN_LOOP_FLAG_CTRL_END_ON_HUP)
18,543✔
273
                    g_mainloop_terminate = true;
4✔
274

275
                if (g_mainloop_terminate)
40,753✔
276
                    break;
277
            }
278

279
            if (pollfds[CTRL_CLIENT_FD].revents & POLLHUP) {
122,196✔
280
                if (ctrlclntfd >= 0)
508✔
281
                    close(ctrlclntfd);
×
282
                ctrlclntfd = -1;
508✔
283
                /* unixio gets this signal, not tcp */
284
                if (mlp->flags & MAIN_LOOP_FLAG_CTRL_END_ON_HUP) {
508✔
285
                    g_mainloop_terminate = true;
×
286
                    break;
×
287
                }
288
            }
289

290
            if (!(pollfds[DATA_CLIENT_FD].revents & POLLIN))
122,196✔
291
                continue;
79,719✔
292

293
            /* before processing a command ensure that the storage is locked */
294
            if ((g_mainloop_terminate = !mainloop_ensure_locked_storage(mlp)))
42,477✔
295
                break;
296

297
            /* Read the command.  The number of bytes is determined by 'paramSize' in the stream */
298
            if (rc == 0) {
42,477✔
299
                rc = SWTPM_IO_Read(&connection_fd, command, &command_length,
42,477✔
300
                                   max_command_length, &mlp->ps);
301
                if (rc != 0) {
42,477✔
302
                    /* connection broke */
303
                    SWTPM_IO_Disconnect(&connection_fd);
20,263✔
304
                }
305
            }
306

307
            cmd_offset = 0;
42,477✔
308
            /* Handle optional TCG Header in front of TPM 2 Command */
309
            if (rc == 0 && mlp->tpmversion == TPMLIB_TPM_VERSION_2) {
42,477✔
310
                cmd_offset = tpmlib_handle_tcg_tpm2_cmd_header(command,
10,102✔
311
                                                               command_length,
312
                                                               &g_locality);
313
                if (cmd_offset > 0) {
10,102✔
314
                    /* send header and trailer */
315
                    iov[0].iov_len = sizeof(respprefix);
6,477✔
316
                    iov[2].iov_len = sizeof(ack);
6,477✔
317
                } else {
318
                    iov[0].iov_len = 0;
3,625✔
319
                    iov[2].iov_len = 0;
3,625✔
320
                }
321
            }
322

323
            if (rc == 0) {
42,477✔
324
                if (!tpm_running) {
22,214✔
325
                    tpmlib_write_fatal_error_response(&rbuffer, &rlength,
14✔
326
                                                      &rTotal,
327
                                                      mlp->tpmversion);
328
                    goto skip_process;
14✔
329
                }
330
            }
331

332
            if (rc == 0) {
42,463✔
333
                lastCommand =
22,200✔
334
                    tpmlib_get_cmd_ordinal(&command[cmd_offset],
44,400✔
335
                                           command_length - cmd_offset);
22,200✔
336
                if (lastCommand != TPM_ORDINAL_NONE)
22,200✔
337
                    mlp->lastCommand = lastCommand;
22,200✔
338
            }
339

340
            if (rc == 0) {
42,463✔
341
                rlength = 0;                                /* clear the response buffer */
22,200✔
342
                rc = tpmlib_process(&rbuffer,
22,200✔
343
                                    &rlength,
344
                                    &rTotal,
345
                                    &command[cmd_offset],
346
                                    command_length - cmd_offset,
347
                                    mlp->locality_flags,
348
                                    &g_locality,
349
                                    mlp->tpmversion);
350
                if (rlength)
22,200✔
351
                    goto skip_process;
×
352
            }
353

354
            if (rc == 0) {
42,463✔
355
                rlength = 0;                                /* clear the response buffer */
22,200✔
356
                rc = TPMLIB_Process(&rbuffer,
22,200✔
357
                                    &rlength,
358
                                    &rTotal,
359
                                    &command[cmd_offset],
360
                                    command_length - cmd_offset);
361
            }
362

363
skip_process:
20,263✔
364
            /* write the results */
365
            if (rc == 0) {
42,477✔
366
                respprefix.size = htobe32(rlength);
22,214✔
367
                iov[1].iov_base = rbuffer;
22,214✔
368
                iov[1].iov_len  = rlength;
22,214✔
369

370
                SWTPM_IO_Write(&connection_fd, iov, ARRAY_LEN(iov),
22,214✔
371
                               &mlp->ps);
372
            }
373

374
            if (!(mlp->flags & MAIN_LOOP_FLAG_KEEP_CONNECTION)) {
42,477✔
375
                SWTPM_IO_Disconnect(&connection_fd);
551✔
376
                break;
551✔
377
            }
378
        }
379

380
        rc = 0; /* A fatal TPM_Process() error should cause the TPM to enter shutdown.  IO errors
21,057✔
381
                   are outside the TPM, so the TPM does not shut down.  The main loop should
382
                   continue to function.*/
383
        if (connection_fd.fd < 0 && mlp->flags & MAIN_LOOP_FLAG_TERMINATE)
21,057✔
384
            break;
385
    }
386

387
    if (tpm_running && !mlp->disable_auto_shutdown)
423✔
388
        tpmlib_maybe_send_tpm2_shutdown(mlp->tpmversion, &mlp->lastCommand,
5✔
389
                                        &mlp->ps);
390

391
    free(rbuffer);
423✔
392
    free(command);
423✔
393

394
    if (ctrlclntfd >= 0)
423✔
395
        close(ctrlclntfd);
415✔
396
    ctrlchannel_set_client_fd(mlp->cc, -1);
423✔
397

398
    if (connection_fd.fd >= 0 && !(mlp->flags & MAIN_LOOP_FLAG_USE_FD))
423✔
UNCOV
399
        close(connection_fd.fd);
×
400

401
    if (mlp->fd >= 0) {
423✔
402
        close(mlp->fd);
391✔
403
        mlp->fd = -1;
391✔
404
    }
405

406
    return rc;
423✔
407
}
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