ipxe / wimboot

WIM bootloader
https://ipxe.org/wimboot
GNU General Public License v2.0
238 stars 42 forks source link

memmap_next might cause an infinite loop #16

Closed ttyS0 closed 3 years ago

ttyS0 commented 3 years ago

I used iPXE with wimboot to boot wim images, the configuration works well except on an old machine, whose components could mainly dateback to 2004 and 2005, BIOS only. It hangs after prompting "Emulating drive 0x81", shown as the figure below.

Hangs after emulating drive

After compilation of wimboot with higher verbose level, I found that it enters an infinite loop, and the message starting with "INT 15,e820" points to the memmap.c section. Tracing the program, the chain is

main.c(main)->paging.c(relocate_memory_high)->memmap.c(memmap_next)

I limited the maximum iteration in paging.c, and added some details in verbose messages of memmap.c:

...
        DBG2 ( "INT 15,e820 region [%llx,%llx) type %d attr %d next=%d\n",
               e820_buf.start, ( e820_buf.start + e820_buf.len ),
               e820_buf.type, e820_buf.attrs, e820_ebx);

        /* Skip non-RAM regions */
        if ( e820_buf.type != E820_TYPE_RAM ) {
            continue;
        }
        if ( params.ecx > offsetof ( typeof ( e820_buf ), attrs ) ) {
            if ( ! ( e820_buf.attrs & E820_ATTR_ENABLED ) )
                continue;
            if ( e820_buf.attrs & E820_ATTR_NONVOLATILE )
                continue;
        }
        DBG2 ( "^^ RETURNED AS E820_BUF\n" );
        /* Return this region */
        return &e820_buf;

    } while ( e820_ebx != 0 );

    DBG2 ( "NULL WILL BE RETURNED\n" );
    return NULL;

IMG_20210221_020310

And compare with a virtual machine messages:

Screenshot_2021-02-21_02-08-38

I can now confirm that there is some bug in memmap.c: as long as the last entry (where [ebx(next) = 0]) returns, then the caller of memmap_next (usually use another while to call) enters an infinite loop.

A noticeable difference of the two figures above is about the map order. In the virtual machine, these maps are sorted, and the last entry will be ignored because it is probably reserved, then e820_ebx != 0 is checked, so memmap_next returns NULL; but in my old machine, these maps are not sorted by address offset, i.e. the last entry is possible to be any type, then it might be returned by memmap_next. Then it causes an infinite loop in paging.c(relocate_memory_high)

while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {