The following failure was reported:

[   10.693310][    T1] tpm_tis STM0925:00: 2.0 TPM (device-id 0x3, rev-id 0)
[   10.848132][    T1] ------------[ cut here ]------------
[   10.853559][    T1] WARNING: CPU: 59 PID: 1 at mm/page_alloc.c:4727 
__alloc_pages_noprof+0x2ca/0x330
[   10.862827][    T1] Modules linked in:
[   10.866671][    T1] CPU: 59 UID: 0 PID: 1 Comm: swapper/0 Not tainted 
6.12.0-lp155.2.g52785e2-default #1 openSUSE Tumbleweed (unreleased) 
588cd98293a7c9eba9013378d807364c088c9375
[   10.882741][    T1] Hardware name: HPE ProLiant DL320 Gen12/ProLiant DL320 
Gen12, BIOS 1.20 10/28/2024
[   10.892170][    T1] RIP: 0010:__alloc_pages_noprof+0x2ca/0x330
[   10.898103][    T1] Code: 24 08 e9 4a fe ff ff e8 34 36 fa ff e9 88 fe ff ff 
83 fe 0a 0f 86 b3 fd ff ff 80 3d 01 e7 ce 01 00 75 09 c6 05 f8 e6 ce 01 01 <0f> 
0b 45 31 ff e9 e5 fe ff ff f7 c2 00 00 08 00 75 42 89 d9 80 e1
[   10.917750][    T1] RSP: 0000:ffffb7cf40077980 EFLAGS: 00010246
[   10.923777][    T1] RAX: 0000000000000000 RBX: 0000000000040cc0 RCX: 
0000000000000000
[   10.931727][    T1] RDX: 0000000000000000 RSI: 000000000000000c RDI: 
0000000000040cc0

Above shows that ACPI pointed a 16 MiB buffer for the log events because
RSI maps to the 'order' parameter of __alloc_pages_noprof(). Address the
bug by mapping the region when needed instead of copying.

Reported-by: Andy Liang <[email protected]>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=219495
Suggested-by: Matthew Garrett <[email protected]>
Signed-off-by: Jarkko Sakkinen <[email protected]>
---
 drivers/char/tpm/eventlog/acpi.c   | 30 +++++--------------
 drivers/char/tpm/eventlog/common.c | 33 ++++++++++++--------
 drivers/char/tpm/eventlog/common.h |  6 ++++
 drivers/char/tpm/eventlog/tpm1.c   | 38 +++++++++++++++--------
 drivers/char/tpm/eventlog/tpm2.c   | 48 +++++++++++++++++++-----------
 include/linux/tpm.h                |  1 +
 6 files changed, 91 insertions(+), 65 deletions(-)

diff --git a/drivers/char/tpm/eventlog/acpi.c b/drivers/char/tpm/eventlog/acpi.c
index 69533d0bfb51..8d8db66ce876 100644
--- a/drivers/char/tpm/eventlog/acpi.c
+++ b/drivers/char/tpm/eventlog/acpi.c
@@ -22,8 +22,6 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <linux/tpm_eventlog.h>
-
-#include "../tpm.h"
 #include "common.h"
 
 struct acpi_tcpa {
@@ -70,14 +68,11 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
        acpi_status status;
        void __iomem *virt;
        u64 len, start;
-       struct tpm_bios_log *log;
        struct acpi_table_tpm2 *tbl;
        struct acpi_tpm2_phy *tpm2_phy;
        int format;
        int ret;
 
-       log = &chip->log;
-
        /* Unfortuntely ACPI does not associate the event log with a specific
         * TPM, like PPI. Thus all ACPI TPMs will read the same log.
         */
@@ -135,36 +130,27 @@ int tpm_read_log_acpi(struct tpm_chip *chip)
                return -EIO;
        }
 
-       /* malloc EventLog space */
-       log->bios_event_log = devm_kmalloc(&chip->dev, len, GFP_KERNEL);
-       if (!log->bios_event_log)
-               return -ENOMEM;
-
-       log->bios_event_log_end = log->bios_event_log + len;
-
        virt = acpi_os_map_iomem(start, len);
        if (!virt) {
                dev_warn(&chip->dev, "%s: Failed to map ACPI memory\n", 
__func__);
                /* try EFI log next */
-               ret = -ENODEV;
-               goto err;
+               return -ENODEV;
        }
 
-       memcpy_fromio(log->bios_event_log, virt, len);
-
-       acpi_os_unmap_iomem(virt, len);
-
-       if (chip->flags & TPM_CHIP_FLAG_TPM2 &&
-           !tpm_is_tpm2_log(log->bios_event_log, len)) {
+       if (chip->flags & TPM_CHIP_FLAG_TPM2 && !tpm_is_tpm2_log(virt, len)) {
+               acpi_os_unmap_iomem(virt, len);
                /* try EFI log next */
                ret = -ENODEV;
                goto err;
        }
 
+       acpi_os_unmap_iomem(virt, len);
+       chip->flags |= TPM_CHIP_FLAG_ACPI_LOG;
+       chip->log.bios_event_log = (void *)start;
+       chip->log.bios_event_log_end = (void *)start + len;
        return format;
 
 err:
-       devm_kfree(&chip->dev, log->bios_event_log);
-       log->bios_event_log = NULL;
+       acpi_os_unmap_iomem(virt, len);
        return ret;
 }
diff --git a/drivers/char/tpm/eventlog/common.c 
b/drivers/char/tpm/eventlog/common.c
index 4c0bbba64ee5..d934ef8c8b7f 100644
--- a/drivers/char/tpm/eventlog/common.c
+++ b/drivers/char/tpm/eventlog/common.c
@@ -25,11 +25,12 @@
 static int tpm_bios_measurements_open(struct inode *inode,
                                            struct file *file)
 {
-       int err;
-       struct seq_file *seq;
+       struct tpm_measurements *priv;
        struct tpm_chip_seqops *chip_seqops;
        const struct seq_operations *seqops;
        struct tpm_chip *chip;
+       struct seq_file *seq;
+       int ret;
 
        inode_lock(inode);
        if (!inode->i_private) {
@@ -42,27 +43,35 @@ static int tpm_bios_measurements_open(struct inode *inode,
        get_device(&chip->dev);
        inode_unlock(inode);
 
-       /* now register seq file */
-       err = seq_open(file, seqops);
-       if (!err) {
-               seq = file->private_data;
-               seq->private = chip;
-       } else {
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+       priv->chip = chip;
+
+       ret = seq_open(file, seqops);
+       if (ret) {
+               kfree(priv);
                put_device(&chip->dev);
+       } else {
+               seq = file->private_data;
+               seq->private = priv;
        }
 
-       return err;
+       return ret;
 }
 
 static int tpm_bios_measurements_release(struct inode *inode,
                                         struct file *file)
 {
        struct seq_file *seq = file->private_data;
-       struct tpm_chip *chip = seq->private;
+       struct tpm_measurements *priv = seq->private;
+       int ret;
 
-       put_device(&chip->dev);
+       put_device(&priv->chip->dev);
+       ret = seq_release(inode, file);
+       kfree(priv);
 
-       return seq_release(inode, file);
+       return ret;
 }
 
 static const struct file_operations tpm_bios_measurements_ops = {
diff --git a/drivers/char/tpm/eventlog/common.h 
b/drivers/char/tpm/eventlog/common.h
index 47ff8136ceb5..ad89a0daf585 100644
--- a/drivers/char/tpm/eventlog/common.h
+++ b/drivers/char/tpm/eventlog/common.h
@@ -7,6 +7,12 @@ extern const struct seq_operations 
tpm1_ascii_b_measurements_seqops;
 extern const struct seq_operations tpm1_binary_b_measurements_seqops;
 extern const struct seq_operations tpm2_binary_b_measurements_seqops;
 
+struct tpm_measurements {
+       struct tpm_chip *chip;
+       void *start;
+       void *end;
+};
+
 #if defined(CONFIG_ACPI)
 int tpm_read_log_acpi(struct tpm_chip *chip);
 #else
diff --git a/drivers/char/tpm/eventlog/tpm1.c b/drivers/char/tpm/eventlog/tpm1.c
index 12ee42a31c71..6141a580e99c 100644
--- a/drivers/char/tpm/eventlog/tpm1.c
+++ b/drivers/char/tpm/eventlog/tpm1.c
@@ -22,11 +22,8 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/tpm_eventlog.h>
-
-#include "../tpm.h"
 #include "common.h"
 
-
 static const char* tcpa_event_type_strings[] = {
        "PREBOOT",
        "POST CODE",
@@ -70,20 +67,32 @@ static const char* tcpa_pc_event_id_strings[] = {
 static void *tpm1_bios_measurements_start(struct seq_file *m, loff_t *pos)
 {
        loff_t i = 0;
-       struct tpm_chip *chip = m->private;
+       struct tpm_measurements *priv = m->private;
+       struct tpm_chip *chip = priv->chip;
        struct tpm_bios_log *log = &chip->log;
-       void *addr = log->bios_event_log;
-       void *limit = log->bios_event_log_end;
        struct tcpa_event *event;
        u32 converted_event_size;
        u32 converted_event_type;
+       size_t log_size;
+       void *addr;
+
+       log_size = log->bios_event_log_end - log->bios_event_log;
+
+       priv->start = !(chip->flags & TPM_CHIP_FLAG_ACPI_LOG) ?
+                     log->bios_event_log :
+                     acpi_os_map_iomem((unsigned long)log->bios_event_log, 
log_size);
+       if (!priv->start)
+               return NULL;
+       priv->end = priv->start + log_size;
+
+       addr = priv->start;
 
        /* read over *pos measurements */
        do {
                event = addr;
 
                /* check if current entry is valid */
-               if (addr + sizeof(struct tcpa_event) > limit)
+               if (addr + sizeof(struct tcpa_event) > priv->end)
                        return NULL;
 
                converted_event_size =
@@ -93,7 +102,7 @@ static void *tpm1_bios_measurements_start(struct seq_file 
*m, loff_t *pos)
 
                if (((converted_event_type == 0) && (converted_event_size == 0))
                    || ((addr + sizeof(struct tcpa_event) + 
converted_event_size)
-                       > limit))
+                       > priv->end))
                        return NULL;
 
                if (i++ == *pos)
@@ -108,10 +117,8 @@ static void *tpm1_bios_measurements_start(struct seq_file 
*m, loff_t *pos)
 static void *tpm1_bios_measurements_next(struct seq_file *m, void *v,
                                        loff_t *pos)
 {
+       struct tpm_measurements *priv = m->private;
        struct tcpa_event *event = v;
-       struct tpm_chip *chip = m->private;
-       struct tpm_bios_log *log = &chip->log;
-       void *limit = log->bios_event_log_end;
        u32 converted_event_size;
        u32 converted_event_type;
 
@@ -121,7 +128,7 @@ static void *tpm1_bios_measurements_next(struct seq_file 
*m, void *v,
        v += sizeof(struct tcpa_event) + converted_event_size;
 
        /* now check if current entry is valid */
-       if ((v + sizeof(struct tcpa_event)) > limit)
+       if ((v + sizeof(struct tcpa_event)) > priv->end)
                return NULL;
 
        event = v;
@@ -130,7 +137,7 @@ static void *tpm1_bios_measurements_next(struct seq_file 
*m, void *v,
        converted_event_type = do_endian_conversion(event->event_type);
 
        if (((converted_event_type == 0) && (converted_event_size == 0)) ||
-           ((v + sizeof(struct tcpa_event) + converted_event_size) > limit))
+           ((v + sizeof(struct tcpa_event) + converted_event_size) > 
priv->end))
                return NULL;
 
        return v;
@@ -138,6 +145,11 @@ static void *tpm1_bios_measurements_next(struct seq_file 
*m, void *v,
 
 static void tpm1_bios_measurements_stop(struct seq_file *m, void *v)
 {
+       struct tpm_measurements *priv = m->private;
+       struct tpm_chip *chip = priv->chip;
+
+       if (!!(chip->flags & TPM_CHIP_FLAG_ACPI_LOG))
+               acpi_os_unmap_iomem(priv->start, priv->end - priv->start);
 }
 
 static int get_event_name(char *dest, struct tcpa_event *event,
diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c
index 37a05800980c..79e090dd751a 100644
--- a/drivers/char/tpm/eventlog/tpm2.c
+++ b/drivers/char/tpm/eventlog/tpm2.c
@@ -12,14 +12,13 @@
  * content.
  */
 
+#include "linux/tpm.h"
 #include <linux/seq_file.h>
 #include <linux/fs.h>
 #include <linux/security.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/tpm_eventlog.h>
-
-#include "../tpm.h"
 #include "common.h"
 
 /*
@@ -41,20 +40,31 @@ static size_t calc_tpm2_event_size(struct 
tcg_pcr_event2_head *event,
 
 static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
 {
-       struct tpm_chip *chip = m->private;
+       struct tpm_measurements *priv = m->private;
+       struct tpm_chip *chip = priv->chip;
        struct tpm_bios_log *log = &chip->log;
-       void *addr = log->bios_event_log;
-       void *limit = log->bios_event_log_end;
        struct tcg_pcr_event *event_header;
        struct tcg_pcr_event2_head *event;
-       size_t size;
+       size_t size, log_size;
+       void *addr;
        int i;
 
+       log_size = log->bios_event_log_end - log->bios_event_log;
+
+       priv->start = !(chip->flags & TPM_CHIP_FLAG_ACPI_LOG) ?
+                     log->bios_event_log :
+                     acpi_os_map_iomem((unsigned long)log->bios_event_log, 
log_size);
+       if (!priv->start)
+               return NULL;
+
+       priv->end = priv->start + log_size;
+
+       addr = priv->start;
        event_header = addr;
        size = struct_size(event_header, event, event_header->event_size);
 
        if (*pos == 0) {
-               if (addr + size < limit) {
+               if (addr + size < priv->end) {
                        if ((event_header->event_type == 0) &&
                            (event_header->event_size == 0))
                                return NULL;
@@ -66,7 +76,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, 
loff_t *pos)
                addr += size;
                event = addr;
                size = calc_tpm2_event_size(event, event_header);
-               if ((addr + size >=  limit) || (size == 0))
+               if ((addr + size >= priv->end) || !size)
                        return NULL;
        }
 
@@ -74,7 +84,7 @@ static void *tpm2_bios_measurements_start(struct seq_file *m, 
loff_t *pos)
                event = addr;
                size = calc_tpm2_event_size(event, event_header);
 
-               if ((addr + size >= limit) || (size == 0))
+               if ((addr + size >= priv->end) || !size)
                        return NULL;
                addr += size;
        }
@@ -85,16 +95,14 @@ static void *tpm2_bios_measurements_start(struct seq_file 
*m, loff_t *pos)
 static void *tpm2_bios_measurements_next(struct seq_file *m, void *v,
                                         loff_t *pos)
 {
+       struct tpm_measurements *priv = m->private;
        struct tcg_pcr_event *event_header;
        struct tcg_pcr_event2_head *event;
-       struct tpm_chip *chip = m->private;
-       struct tpm_bios_log *log = &chip->log;
-       void *limit = log->bios_event_log_end;
        size_t event_size;
        void *marker;
 
        (*pos)++;
-       event_header = log->bios_event_log;
+       event_header = priv->start;
 
        if (v == SEQ_START_TOKEN) {
                event_size = struct_size(event_header, event,
@@ -109,13 +117,13 @@ static void *tpm2_bios_measurements_next(struct seq_file 
*m, void *v,
        }
 
        marker = marker + event_size;
-       if (marker >= limit)
+       if (marker >= priv->end)
                return NULL;
        v = marker;
        event = v;
 
        event_size = calc_tpm2_event_size(event, event_header);
-       if (((v + event_size) >= limit) || (event_size == 0))
+       if (((v + event_size) >= priv->end) || !event_size)
                return NULL;
 
        return v;
@@ -123,13 +131,17 @@ static void *tpm2_bios_measurements_next(struct seq_file 
*m, void *v,
 
 static void tpm2_bios_measurements_stop(struct seq_file *m, void *v)
 {
+       struct tpm_measurements *priv = m->private;
+       struct tpm_chip *chip = priv->chip;
+
+       if (!!(chip->flags & TPM_CHIP_FLAG_ACPI_LOG))
+               acpi_os_unmap_iomem(priv->start, priv->end - priv->start);
 }
 
 static int tpm2_binary_bios_measurements_show(struct seq_file *m, void *v)
 {
-       struct tpm_chip *chip = m->private;
-       struct tpm_bios_log *log = &chip->log;
-       struct tcg_pcr_event *event_header = log->bios_event_log;
+       struct tpm_measurements *priv = m->private;
+       struct tcg_pcr_event *event_header = priv->start;
        struct tcg_pcr_event2_head *event = v;
        void *temp_ptr;
        size_t size;
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index 20a40ade8030..f3d12738b93b 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -348,6 +348,7 @@ enum tpm_chip_flags {
        TPM_CHIP_FLAG_SUSPENDED                 = BIT(8),
        TPM_CHIP_FLAG_HWRNG_DISABLED            = BIT(9),
        TPM_CHIP_FLAG_DISABLE                   = BIT(10),
+       TPM_CHIP_FLAG_ACPI_LOG          = BIT(11),
 };
 
 #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev)
-- 
2.47.1


Reply via email to