On Tue, 2021-01-26 at 12:22 +0000, Dr. David Alan Gilbert wrote:
> * James Bottomley (j...@linux.ibm.com) wrote:
> > OVMF is developing a mechanism for depositing a GUIDed table just
> > below the known location of the reset vector.  The table goes
> > backwards in memory so all entries are of the form
> > 
> > <data>|len|<GUID>
> > 
> > Where <data> is arbtrary size and type, <len> is a uint16_t and
> > describes the entire length of the entry from the beginning of the
> > data to the end of the guid.
> > 
> > The foot of the table is of this form and <len> for this case
> > describes the entire size of the table.  The table foot GUID is
> > defined by OVMF as 96b582de-1fb2-45f7-baea-a366c55a082d and if the
> > table is present this GUID is just below the reset vector, 48 bytes
> > before the end of the firmware file.
> > 
> > Add a parser for the ovmf reset block which takes a copy of the
> > block,
> > if the table foot guid is found, minus the footer and a function
> > for
> > later traversal to return the data area of any specified GUIDs.
> > 
> > Signed-off-by: James Bottomley <j...@linux.ibm.com>
> > 
> > ---
> > 
> > v2: fix brace warnings and return values
> > ---
> >  hw/i386/pc_sysfw.c   | 106
> > +++++++++++++++++++++++++++++++++++++++++++
> >  include/hw/i386/pc.h |   4 ++
> >  2 files changed, 110 insertions(+)
> > 
> > diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
> > index 92e90ff013..436b78c587 100644
> > --- a/hw/i386/pc_sysfw.c
> > +++ b/hw/i386/pc_sysfw.c
> > @@ -124,6 +124,107 @@ void
> > pc_system_flash_cleanup_unused(PCMachineState *pcms)
> >      }
> >  }
> >  
> > +#define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-
> > a366c55a082d"
> > +
> > +static uint8_t *ovmf_table;
> > +static int ovmf_table_len;
> > +
> > +static void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, int
> > flash_size)
> 
> Maybe size_t for flash_size?

Heh, sure, who knows how big OVMF will get ...  but I get the point
about an int overflow attack.

> > +{
> > +    uint8_t *ptr;
> > +    QemuUUID guid;
> > +    int tot_len;
> > +
> > +    /* should only be called once */
> > +    if (ovmf_table) {
> > +        return;
> > +    }
> > +
> > +    /*
> > +     * if this is OVMF there will be a table footer
> > +     * guid 48 bytes before the end of the flash file.  If it's
> > +     * not found, silently abort the flash parsing.
> > +     */
> > +    qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid);
> > +    guid = qemu_uuid_bswap(guid); /* guids are LE */
> > +    ptr = flash_ptr + flash_size - 48;
> 
> I think since flash_size is coming from memory_region_size it's
> probably rounded to a page size by now, but perhaps we should always
> check we have enough space before we start moving pointers around

I think OVMF must be at least a page, so I can add that check.

> (Given that the OVMF binary might be provided by the guest owner, we
> have to consider it might be a vector to attack the hypervisor).
> 
> > +    if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) {
> > +        return;
> > +    }
> > +
> > +    /* if found, just before is two byte table length */
> > +    ptr -= sizeof(uint16_t);
> > +    tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) -
> > sizeof(uint16_t);
> > +
> > +    if (tot_len <= 0) {
> > +        return;
> > +    }
> > +
> > +    ovmf_table = g_malloc(tot_len);
> > +    ovmf_table_len = tot_len;
> > +
> > +    /*
> > +     * ptr is the foot of the table, so copy it all to the newly
> > +     * allocated ovmf_table and then set the ovmf_table pointer
> > +     * to the table foot
> > +     */
> > +    memcpy(ovmf_table, ptr - tot_len, tot_len);
> > +    ovmf_table += tot_len;
> > +}
> > +
> > +bool pc_system_ovmf_table_find(const char *entry, uint8_t **data,
> > +                               int *data_len)
> > +{
> > +    uint8_t *ptr = ovmf_table;
> > +    int tot_len = ovmf_table_len;
> > +    QemuUUID entry_guid;
> > +
> > +    if (qemu_uuid_parse(entry, &entry_guid) < 0) {
> > +        return false;
> > +    }
> > +
> > +    if (!ptr) {
> > +        return false;
> > +    }
> > +
> > +    entry_guid = qemu_uuid_bswap(entry_guid); /* guids are LE */
> > +    while (tot_len > 0) {
> > +        int len;
> > +        QemuUUID *guid;
> > +
> > +        /*
> > +         * The data structure is
> > +         *   arbitrary length data
> > +         *   2 byte length of entire entry
> > +         *   16 byte guid
> > +         */
> > +        guid = (QemuUUID *)(ptr - sizeof(QemuUUID));
> > +        len = le16_to_cpu(*(uint16_t *)(ptr - sizeof(QemuUUID) -
> > +                                        sizeof(uint16_t)));
> 
> Again I think we need to be checking tot_len > (sizeof(QemuUUID) +
> sizeof(uint16_t)) before doing this dereference.

I can make the loop start

  while (tot_len > sizeof(QemuUUID) + sizeof(uint16_t))

> > +        /*
> > +         * just in case the table is corrupt, wouldn't want to
> > spin in
> > +         * the zero case
> > +         */
> > +        if (len < sizeof(QemuUUID) + sizeof(uint16_t)) {
> > +                return false;
> > +        }
> > +
> > +        ptr -= len;
> > +        tot_len -= len;
> 
> and that len is smaller or equal to tot_len here.

OK.



Reply via email to