• Vladis Dronov's avatar
    efi: Fix a race and a buffer overflow while reading efivars via sysfs · 286d3250
    Vladis Dronov authored
    There is a race and a buffer overflow corrupting a kernel memory while
    reading an EFI variable with a size more than 1024 bytes via the older
    sysfs method. This happens because accessing struct efi_variable in
    efivar_{attr,size,data}_read() and friends is not protected from
    a concurrent access leading to a kernel memory corruption and, at best,
    to a crash. The race scenario is the following:
    
    CPU0:                                CPU1:
    efivar_attr_read()
      var->DataSize = 1024;
      efivar_entry_get(... &var->DataSize)
        down_interruptible(&efivars_lock)
                                         efivar_attr_read() // same EFI var
                                           var->DataSize = 1024;
                                           efivar_entry_get(... &var->DataSize)
                                             down_interruptible(&efivars_lock)
        virt_efi_get_variable()
        // returns EFI_BUFFER_TOO_SMALL but
        // var->DataSize is set to a real
        // var size more than 1024 bytes
        up(&efivars_lock)
                                             virt_efi_get_variable()
                                             // called with var->DataSize set
                                             // to a real var size, returns
                                             // successfully and overwrites
                                             // a 1024-bytes kernel buffer
                                             up(&efivars_lock)
    
    This can be reproduced by concurrent reading of an EFI variable which size
    is more than 1024 bytes:
    
      ts# for cpu in $(seq 0 $(nproc --ignore=1)); do ( taskset -c $cpu \
      cat /sys/firmware/efi/vars/KEKDefault*/size & ) ; done
    
    Fix this by using a local variable for a var's data buffer size so it
    does not get overwritten.
    
    Fixes: e14ab23d ("efivars: efivar_entry API")
    Reported-by: Bob Sanders <bob.sanders@hpe.com> and the LTP testsuite
    Signed-off-by: default avatarVladis Dronov <vdronov@redhat.com>
    Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    Cc: <stable@vger.kernel.org>
    Link: https://lore.kernel.org/r/20200305084041.24053-2-vdronov@redhat.com
    Link: https://lore.kernel.org/r/20200308080859.21568-24-ardb@kernel.org
    286d3250
efivars.c 16.6 KB