Michael Chang 30708dfe3b tpm: Disable the tpm verifier if the TPM device is not present
When the tpm module is loaded, the verifier reads entire file into
memory, measures it and uses verified content as a backing buffer for
file accesses. However, this process may result in high memory
utilization for file operations, sometimes causing a system to run out
of memory which may finally lead to boot failure. To address this issue,
among others, the commit 887f98f0d (mm: Allow dynamically requesting
additional memory regions) have optimized memory management by
dynamically allocating heap space to maximize memory usage and reduce
threat of memory exhaustion. But in some cases problems may still arise,
e.g., when large ISO images are mounted using loopback or when dealing
with embedded systems with limited memory resources.

Unfortunately current implementation of the tpm module doesn't allow
elimination of the back buffer once it is loaded. Even if the TPM device
is not present or it has been explicitly disabled. This may unnecessary
allocate a lot memory. To solve this issue, a patch has been developed
to detect the TPM status at module load and skip verifier registration
if the device is missing or deactivated. This prevents allocation of
memory for the back buffer, avoiding wasting memory when no real measure
boot functionality is performed. Disabling the TPM device in the system
can reduce memory usage in the GRUB. It is useful in scenarios where
high memory utilization is a concern and measurements of loaded
artifacts are not necessary.

Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
2023-03-29 20:35:05 +02:00

123 lines
3.4 KiB
C

/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 2018 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*
* Core TPM support code.
*/
#include <grub/env.h>
#include <grub/err.h>
#include <grub/i18n.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/tpm.h>
#include <grub/term.h>
#include <grub/verify.h>
#include <grub/dl.h>
GRUB_MOD_LICENSE ("GPLv3+");
static grub_err_t
grub_tpm_verify_init (grub_file_t io,
enum grub_file_type type __attribute__ ((unused)),
void **context, enum grub_verify_flags *flags)
{
*context = io->name;
*flags |= GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
return GRUB_ERR_NONE;
}
static inline bool
is_tpm_fail_fatal (void)
{
return grub_env_get_bool ("tpm_fail_fatal", false);
}
static grub_err_t
grub_tpm_verify_write (void *context, void *buf, grub_size_t size)
{
grub_err_t status = grub_tpm_measure (buf, size, GRUB_BINARY_PCR, context);
if (status == GRUB_ERR_NONE)
return GRUB_ERR_NONE;
grub_dprintf ("tpm", "Measuring buffer failed: %d\n", status);
return is_tpm_fail_fatal () ? status : GRUB_ERR_NONE;
}
static grub_err_t
grub_tpm_verify_string (char *str, enum grub_verify_string_type type)
{
const char *prefix = NULL;
char *description;
grub_err_t status;
switch (type)
{
case GRUB_VERIFY_KERNEL_CMDLINE:
prefix = "kernel_cmdline: ";
break;
case GRUB_VERIFY_MODULE_CMDLINE:
prefix = "module_cmdline: ";
break;
case GRUB_VERIFY_COMMAND:
prefix = "grub_cmd: ";
break;
}
description = grub_malloc (grub_strlen (str) + grub_strlen (prefix) + 1);
if (!description)
return grub_errno;
grub_memcpy (description, prefix, grub_strlen (prefix));
grub_memcpy (description + grub_strlen (prefix), str,
grub_strlen (str) + 1);
status =
grub_tpm_measure ((unsigned char *) str, grub_strlen (str),
GRUB_STRING_PCR, description);
grub_free (description);
if (status == GRUB_ERR_NONE)
return GRUB_ERR_NONE;
grub_dprintf ("tpm", "Measuring string %s failed: %d\n", str, status);
return is_tpm_fail_fatal () ? status : GRUB_ERR_NONE;
}
struct grub_file_verifier grub_tpm_verifier = {
.name = "tpm",
.init = grub_tpm_verify_init,
.write = grub_tpm_verify_write,
.verify_string = grub_tpm_verify_string,
};
GRUB_MOD_INIT (tpm)
{
/*
* Even though this now calls ibmvtpm's grub_tpm_present() from GRUB_MOD_INIT(),
* it does seem to call it late enough in the initialization sequence so
* that whatever discovered "device nodes" before this GRUB_MOD_INIT() is
* called, enables the ibmvtpm driver to see the device nodes.
*/
if (!grub_tpm_present())
return;
grub_verifier_register (&grub_tpm_verifier);
}
GRUB_MOD_FINI (tpm)
{
if (!grub_tpm_present())
return;
grub_verifier_unregister (&grub_tpm_verifier);
}