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>
123 lines
3.4 KiB
C
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);
|
|
}
|