Re: [PATCH] video: fbdev: sis: fix a missing-check bug

From: Wenwen Wang
Date: Mon Oct 29 2018 - 15:09:21 EST


Hello,

Can anyone please confirm this bug? Thanks!

Wenwen

On Fri, Oct 19, 2018 at 3:39 PM Wenwen Wang <wang6495@xxxxxxx> wrote:
>
> In sisfb_find_rom(), the official pci ROM is checked to see whether it
> contains the expected value at specific locations. This is done by firstly
> mapping the rom into the IO memory region 'rom_base' and then invoking
> sisfb_check_rom() to perform the checks. If the checks succeed, i.e.,
> sisfb_check_rom() returns 1, the whole content of the rom is then copied to
> 'myrombase' through memcpy_fromio(). The problem here is that the checks
> are conducted on the IO region 'rom_base' directly. Given that the device
> also has the permission to access the IO region, it is possible that a
> malicious device controlled by an attacker can race to modify the values in
> the region after the checks but before memcpy_fromio(). By doing so, the
> attacker can supply unexpected roms, which can cause undefined behavior of
> the kernel and introduce potential security risk. The following for loop
> also has a similar issue.
>
> To avoid the above issue, this patch firstly copies the content of the rom
> and then performs the checks on the copied version. If all the checks are
> satisfied, the copied version will then be returned.
>
> Signed-off-by: Wenwen Wang <wang6495@xxxxxxx>
> ---
> drivers/video/fbdev/sis/sis_main.c | 52 ++++++++++++++++----------------------
> 1 file changed, 22 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/video/fbdev/sis/sis_main.c b/drivers/video/fbdev/sis/sis_main.c
> index 20aff90..a2d8051 100644
> --- a/drivers/video/fbdev/sis/sis_main.c
> +++ b/drivers/video/fbdev/sis/sis_main.c
> @@ -4089,29 +4089,29 @@ static int __init sisfb_setup(char *options)
> }
> #endif
>
> -static int sisfb_check_rom(void __iomem *rom_base,
> +static int sisfb_check_rom(unsigned char *rom_base,
> struct sis_video_info *ivideo)
> {
> - void __iomem *rom;
> + unsigned char *rom;
> int romptr;
>
> - if((readb(rom_base) != 0x55) || (readb(rom_base + 1) != 0xaa))
> + if ((*rom_base != 0x55) || (*(rom_base + 1) != 0xaa))
> return 0;
>
> - romptr = (readb(rom_base + 0x18) | (readb(rom_base + 0x19) << 8));
> + romptr = (*(rom_base + 0x18) | (*(rom_base + 0x19) << 8));
> if(romptr > (0x10000 - 8))
> return 0;
>
> rom = rom_base + romptr;
>
> - if((readb(rom) != 'P') || (readb(rom + 1) != 'C') ||
> - (readb(rom + 2) != 'I') || (readb(rom + 3) != 'R'))
> + if ((*(rom) != 'P') || (*(rom + 1) != 'C') ||
> + (*(rom + 2) != 'I') || (*(rom + 3) != 'R'))
> return 0;
>
> - if((readb(rom + 4) | (readb(rom + 5) << 8)) != ivideo->chip_vendor)
> + if ((*(rom + 4) | (*(rom + 5) << 8)) != ivideo->chip_vendor)
> return 0;
>
> - if((readb(rom + 6) | (readb(rom + 7) << 8)) != ivideo->chip_id)
> + if ((*(rom + 6) | (*(rom + 7) << 8)) != ivideo->chip_id)
> return 0;
>
> return 1;
> @@ -4124,6 +4124,10 @@ static unsigned char *sisfb_find_rom(struct pci_dev *pdev)
> unsigned char *myrombase = NULL;
> size_t romsize;
>
> + myrombase = vmalloc(65536);
> + if (!myrombase)
> + return NULL;
> +
> /* First, try the official pci ROM functions (except
> * on integrated chipsets which have no ROM).
> */
> @@ -4131,20 +4135,15 @@ static unsigned char *sisfb_find_rom(struct pci_dev *pdev)
> if(!ivideo->nbridge) {
>
> if((rom_base = pci_map_rom(pdev, &romsize))) {
> -
> - if(sisfb_check_rom(rom_base, ivideo)) {
> -
> - if((myrombase = vmalloc(65536))) {
> - memcpy_fromio(myrombase, rom_base,
> - (romsize > 65536) ? 65536 : romsize);
> - }
> - }
> + memcpy_fromio(myrombase, rom_base,
> + (romsize > 65536) ? 65536 : romsize);
> pci_unmap_rom(pdev, rom_base);
> +
> + if (sisfb_check_rom(myrombase, ivideo))
> + return myrombase;
> }
> }
>
> - if(myrombase) return myrombase;
> -
> /* Otherwise do it the conventional way. */
>
> #if defined(__i386__) || defined(__x86_64__)
> @@ -4156,24 +4155,17 @@ static unsigned char *sisfb_find_rom(struct pci_dev *pdev)
> rom_base = ioremap(temp, 65536);
> if (!rom_base)
> continue;
> -
> - if (!sisfb_check_rom(rom_base, ivideo)) {
> - iounmap(rom_base);
> - continue;
> - }
> -
> - if ((myrombase = vmalloc(65536)))
> - memcpy_fromio(myrombase, rom_base, 65536);
> -
> + memcpy_fromio(myrombase, rom_base, 65536);
> iounmap(rom_base);
> - break;
>
> + if (sisfb_check_rom(myrombase, ivideo))
> + return myrombase;
> }
>
> }
> #endif
> -
> - return myrombase;
> + vfree(myrombase);
> + return NULL;
> }
>
> static void sisfb_post_map_vram(struct sis_video_info *ivideo,
> --
> 2.7.4
>