linux_dsm_epyc7002/include/linux/tpm_eventlog.h
Gustavo A. R. Silva ab91c2a89f tpm: eventlog: Replace zero-length array with flexible-array member
The current codebase makes use of the zero-length array language
extension to the C90 standard, but the preferred mechanism to declare
variable-length types such as these ones is a flexible array member[1][2],
introduced in C99:

struct foo {
        int stuff;
        struct boo array[];
};

By making use of the mechanism above, we will get a compiler warning
in case the flexible array does not occur last in the structure, which
will help us prevent some kind of undefined behavior bugs from being
inadvertently introduced[3] to the codebase from now on.

Also, notice that, dynamic memory allocations won't be affected by
this change:

"Flexible array members have incomplete type, and so the sizeof operator
may not be applied. As a quirk of the original implementation of
zero-length arrays, sizeof evaluates to zero."[1]

sizeof(flexible-array-member) triggers a warning because flexible array
members have incomplete type[1]. There are some instances of code in
which the sizeof operator is being incorrectly/erroneously applied to
zero-length arrays and the result is zero. Such instances may be hiding
some bugs. So, this work (flexible-array member conversions) will also
help to get completely rid of those sorts of issues.

Also, the following issue shows up due to the flexible-array member
having incomplete type[4]:

drivers/char/tpm/eventlog/tpm2.c: In function ‘tpm2_bios_measurements_start’:
drivers/char/tpm/eventlog/tpm2.c:54:46: error: invalid application of ‘sizeof’ to incomplete type ‘u8[]’ {aka ‘unsigned char[]’}
   54 |  size = sizeof(struct tcg_pcr_event) - sizeof(event_header->event)
      |                                              ^
drivers/char/tpm/eventlog/tpm2.c: In function ‘tpm2_bios_measurements_next’:
drivers/char/tpm/eventlog/tpm2.c:102:10: error: invalid application of ‘sizeof’ to incomplete type ‘u8[]’ {aka ‘unsigned char[]’}
  102 |    sizeof(event_header->event) + event_header->event_size;
      |          ^
drivers/char/tpm/eventlog/tpm2.c: In function ‘tpm2_binary_bios_measurements_show’:
drivers/char/tpm/eventlog/tpm2.c:140:10: error: invalid application of ‘sizeof’ to incomplete type ‘u8[]’ {aka ‘unsigned char[]’}
  140 |    sizeof(event_header->event) + event_header->event_size;
      |          ^
scripts/Makefile.build:266: recipe for target 'drivers/char/tpm/eventlog/tpm2.o' failed
make[3]: *** [drivers/char/tpm/eventlog/tpm2.o] Error 1

As mentioned above: "Flexible array members have incomplete type, and
so the sizeof operator may not be applied. As a quirk of the original
implementation of zero-length arrays, sizeof evaluates to zero."[1] As
in "sizeof(event_header->event) always evaluated to 0, so removing it
has no effect".

Lastly, make use of the struct_size() helper to deal with the
flexible array member and its host structure.

This issue was found with the help of Coccinelle.

[1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
[2] https://github.com/KSPP/linux/issues/21
[3] commit 7649773293 ("cxgb3/l2t: Fix undefined behaviour")
[4] https://github.com/KSPP/linux/issues/43

Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
2020-05-22 18:50:12 +03:00

276 lines
5.8 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_TPM_EVENTLOG_H__
#define __LINUX_TPM_EVENTLOG_H__
#include <linux/tpm.h>
#define TCG_EVENT_NAME_LEN_MAX 255
#define MAX_TEXT_EVENT 1000 /* Max event string length */
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x1
#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x2
#ifdef CONFIG_PPC64
#define do_endian_conversion(x) be32_to_cpu(x)
#else
#define do_endian_conversion(x) x
#endif
enum bios_platform_class {
BIOS_CLIENT = 0x00,
BIOS_SERVER = 0x01,
};
struct tcpa_event {
u32 pcr_index;
u32 event_type;
u8 pcr_value[20]; /* SHA1 */
u32 event_size;
u8 event_data[];
};
enum tcpa_event_types {
PREBOOT = 0,
POST_CODE,
UNUSED,
NO_ACTION,
SEPARATOR,
ACTION,
EVENT_TAG,
SCRTM_CONTENTS,
SCRTM_VERSION,
CPU_MICROCODE,
PLATFORM_CONFIG_FLAGS,
TABLE_OF_DEVICES,
COMPACT_HASH,
IPL,
IPL_PARTITION_DATA,
NONHOST_CODE,
NONHOST_CONFIG,
NONHOST_INFO,
};
struct tcpa_pc_event {
u32 event_id;
u32 event_size;
u8 event_data[];
};
enum tcpa_pc_event_ids {
SMBIOS = 1,
BIS_CERT,
POST_BIOS_ROM,
ESCD,
CMOS,
NVRAM,
OPTION_ROM_EXEC,
OPTION_ROM_CONFIG,
OPTION_ROM_MICROCODE = 10,
S_CRTM_VERSION,
S_CRTM_CONTENTS,
POST_CONTENTS,
HOST_TABLE_OF_DEVICES,
};
/* http://www.trustedcomputinggroup.org/tcg-efi-protocol-specification/ */
struct tcg_efi_specid_event_algs {
u16 alg_id;
u16 digest_size;
} __packed;
struct tcg_efi_specid_event_head {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintnsize;
u32 num_algs;
struct tcg_efi_specid_event_algs digest_sizes[];
} __packed;
struct tcg_pcr_event {
u32 pcr_idx;
u32 event_type;
u8 digest[20];
u32 event_size;
u8 event[];
} __packed;
struct tcg_event_field {
u32 event_size;
u8 event[];
} __packed;
struct tcg_pcr_event2_head {
u32 pcr_idx;
u32 event_type;
u32 count;
struct tpm_digest digests[];
} __packed;
struct tcg_algorithm_size {
u16 algorithm_id;
u16 algorithm_size;
};
struct tcg_algorithm_info {
u8 signature[16];
u32 platform_class;
u8 spec_version_minor;
u8 spec_version_major;
u8 spec_errata;
u8 uintn_size;
u32 number_of_algorithms;
struct tcg_algorithm_size digest_sizes[];
};
#ifndef TPM_MEMREMAP
#define TPM_MEMREMAP(start, size) NULL
#endif
#ifndef TPM_MEMUNMAP
#define TPM_MEMUNMAP(start, size) do{} while(0)
#endif
/**
* __calc_tpm2_event_size - calculate the size of a TPM2 event log entry
* @event: Pointer to the event whose size should be calculated
* @event_header: Pointer to the initial event containing the digest lengths
* @do_mapping: Whether or not the event needs to be mapped
*
* The TPM2 event log format can contain multiple digests corresponding to
* separate PCR banks, and also contains a variable length of the data that
* was measured. This requires knowledge of how long each digest type is,
* and this information is contained within the first event in the log.
*
* We calculate the length by examining the number of events, and then looking
* at each event in turn to determine how much space is used for events in
* total. Once we've done this we know the offset of the data length field,
* and can calculate the total size of the event.
*
* Return: size of the event on success, 0 on failure
*/
static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
struct tcg_pcr_event *event_header,
bool do_mapping)
{
struct tcg_efi_specid_event_head *efispecid;
struct tcg_event_field *event_field;
void *mapping = NULL;
int mapping_size;
void *marker;
void *marker_start;
u32 halg_size;
size_t size;
u16 halg;
int i;
int j;
u32 count, event_type;
marker = event;
marker_start = marker;
marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+ sizeof(event->count);
/* Map the event header */
if (do_mapping) {
mapping_size = marker - marker_start;
mapping = TPM_MEMREMAP((unsigned long)marker_start,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker_start;
}
event = (struct tcg_pcr_event2_head *)mapping;
/*
* The loop below will unmap these fields if the log is larger than
* one page, so save them here for reference:
*/
count = READ_ONCE(event->count);
event_type = READ_ONCE(event->event_type);
efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
/* Check if event is malformed. */
if (count > efispecid->num_algs) {
size = 0;
goto out;
}
for (i = 0; i < count; i++) {
halg_size = sizeof(event->digests[i].alg_id);
/* Map the digest's algorithm identifier */
if (do_mapping) {
TPM_MEMUNMAP(mapping, mapping_size);
mapping_size = halg_size;
mapping = TPM_MEMREMAP((unsigned long)marker,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker;
}
memcpy(&halg, mapping, halg_size);
marker = marker + halg_size;
for (j = 0; j < efispecid->num_algs; j++) {
if (halg == efispecid->digest_sizes[j].alg_id) {
marker +=
efispecid->digest_sizes[j].digest_size;
break;
}
}
/* Algorithm without known length. Such event is unparseable. */
if (j == efispecid->num_algs) {
size = 0;
goto out;
}
}
/*
* Map the event size - we don't read from the event itself, so
* we don't need to map it
*/
if (do_mapping) {
TPM_MEMUNMAP(mapping, mapping_size);
mapping_size += sizeof(event_field->event_size);
mapping = TPM_MEMREMAP((unsigned long)marker,
mapping_size);
if (!mapping) {
size = 0;
goto out;
}
} else {
mapping = marker;
}
event_field = (struct tcg_event_field *)mapping;
marker = marker + sizeof(event_field->event_size)
+ event_field->event_size;
size = marker - marker_start;
if (event_type == 0 && event_field->event_size == 0)
size = 0;
out:
if (do_mapping)
TPM_MEMUNMAP(mapping, mapping_size);
return size;
}
#endif