The fw_cfg DMA write callback in ramfb prepares a new display surface in
QEMU; this new surface is put to use ("swapped in") upon the next display
update. At that time, the old surface (if any) is released.
If the guest triggers the fw_cfg DMA write callback at least twice between
two adjacent display updates, then the second callback (and further such
callbacks) will leak the previously prepared (but not yet swapped in)
display surface.
The issue can be shown by:
(1) starting QEMU with "-trace displaysurface_free", and
(2) running the following program in the guest UEFI shell:
> #include <Library/ShellCEntryLib.h> // ShellAppMain()
> #include <Library/UefiBootServicesTableLib.h> // gBS
> #include <Protocol/GraphicsOutput.h> // EFI_GRAPHICS_OUTPUT_PROTOCOL
>
> INTN
> EFIAPI
> ShellAppMain (
> IN UINTN Argc,
> IN CHAR16 **Argv
> )
> {
> EFI_STATUS Status;
> VOID *Interface;
> EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;
> UINT32 Mode;
>
> Status = gBS->LocateProtocol (
> &gEfiGraphicsOutputProtocolGuid,
> NULL,
> &Interface
> );
> if (EFI_ERROR (Status)) {
> return 1;
> }
>
> Gop = Interface;
>
> Mode = 1;
> for ( ; ;) {
> Status = Gop->SetMode (Gop, Mode);
> if (EFI_ERROR (Status)) {
> break;
> }
>
> Mode = 1 - Mode;
> }
>
> return 1;
> }
The symptom is then that:
- only one trace message appears periodically,
- the time between adjacent messages keeps increasing -- implying that
some list structure (containing the leaked resources) keeps growing,
- the "surface" pointer is ever different.
> 18566@
1695127471.449586:displaysurface_free surface=0x7f2fcc09a7c0
> 18566@
1695127471.529559:displaysurface_free surface=0x7f2fcc9dac10
> 18566@
1695127471.659812:displaysurface_free surface=0x7f2fcc441dd0
> 18566@
1695127471.839669:displaysurface_free surface=0x7f2fcc0363d0
> 18566@
1695127472.069674:displaysurface_free surface=0x7f2fcc413a80
> 18566@
1695127472.349580:displaysurface_free surface=0x7f2fcc09cd00
> 18566@
1695127472.679783:displaysurface_free surface=0x7f2fcc1395f0
> 18566@
1695127473.059848:displaysurface_free surface=0x7f2fcc1cae50
> 18566@
1695127473.489724:displaysurface_free surface=0x7f2fcc42fc50
> 18566@
1695127473.969791:displaysurface_free surface=0x7f2fcc45dcc0
> 18566@
1695127474.499708:displaysurface_free surface=0x7f2fcc70b9d0
> 18566@
1695127475.079769:displaysurface_free surface=0x7f2fcc82acc0
> 18566@
1695127475.709941:displaysurface_free surface=0x7f2fcc369c00
> 18566@
1695127476.389619:displaysurface_free surface=0x7f2fcc32b910
> 18566@
1695127477.119772:displaysurface_free surface=0x7f2fcc0d5a20
> 18566@
1695127477.899517:displaysurface_free surface=0x7f2fcc086c40
> 18566@
1695127478.729962:displaysurface_free surface=0x7f2fccc72020
> 18566@
1695127479.609839:displaysurface_free surface=0x7f2fcc185160
> 18566@
1695127480.539688:displaysurface_free surface=0x7f2fcc23a7e0
> 18566@
1695127481.519759:displaysurface_free surface=0x7f2fcc3ec870
> 18566@
1695127482.549930:displaysurface_free surface=0x7f2fcc634960
> 18566@
1695127483.629661:displaysurface_free surface=0x7f2fcc26b140
> 18566@
1695127484.759987:displaysurface_free surface=0x7f2fcc321700
> 18566@
1695127485.940289:displaysurface_free surface=0x7f2fccaad100
We figured this wasn't a CVE-worthy problem, as only small amounts of
memory were leaked (the framebuffer itself is mapped from guest RAM, QEMU
only allocates administrative structures), plus libvirt restricts QEMU
memory footprint anyway, thus the guest can only DoS itself.
Plug the leak, by releasing the last prepared (not yet swapped in) display
surface, if any, in the fw_cfg DMA write callback.
Regarding the "reproducer", with the fix in place, the log is flooded with
trace messages (one per fw_cfg write), *and* the trace message alternates
between just two "surface" pointer values (i.e., nothing is leaked, the
allocator flip-flops between two objects in effect).
This issue appears to date back to the introducion of ramfb (
995b30179bdc,
"hw/display: add ramfb, a simple boot framebuffer living in guest ram",
2018-06-18).
Cc: Gerd Hoffmann <kraxel@redhat.com> (maintainer:ramfb)
Cc: qemu-stable@nongnu.org
Fixes:
995b30179bdc
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <
20230919131955.27223-1-lersek@redhat.com>