Revision 513dc792d6060d5ef572e43852683097a8420f56 authored by Zhang Xiaoxu on 04 March 2020, 02:24:29 UTC, committed by Daniel Vetter on 06 March 2020, 20:06:34 UTC
When syzkaller tests, there is a UAF: BUG: KASan: use after free in vgacon_invert_region+0x9d/0x110 at addr ffff880000100000 Read of size 2 by task syz-executor.1/16489 page:ffffea0000004000 count:0 mapcount:-127 mapping: (null) index:0x0 page flags: 0xfffff00000000() page dumped because: kasan: bad access detected CPU: 1 PID: 16489 Comm: syz-executor.1 Not tainted Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.9.3-0-ge2fc41e-prebuilt.qemu-project.org 04/01/2014 Call Trace: [<ffffffffb119f309>] dump_stack+0x1e/0x20 [<ffffffffb04af957>] kasan_report+0x577/0x950 [<ffffffffb04ae652>] __asan_load2+0x62/0x80 [<ffffffffb090f26d>] vgacon_invert_region+0x9d/0x110 [<ffffffffb0a39d95>] invert_screen+0xe5/0x470 [<ffffffffb0a21dcb>] set_selection+0x44b/0x12f0 [<ffffffffb0a3bfae>] tioclinux+0xee/0x490 [<ffffffffb0a1d114>] vt_ioctl+0xff4/0x2670 [<ffffffffb0a0089a>] tty_ioctl+0x46a/0x1a10 [<ffffffffb052db3d>] do_vfs_ioctl+0x5bd/0xc40 [<ffffffffb052e2f2>] SyS_ioctl+0x132/0x170 [<ffffffffb11c9b1b>] system_call_fastpath+0x22/0x27 Memory state around the buggy address: ffff8800000fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8800000fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff880000100000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff It can be reproduce in the linux mainline by the program: #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <linux/vt.h> struct tiocl_selection { unsigned short xs; /* X start */ unsigned short ys; /* Y start */ unsigned short xe; /* X end */ unsigned short ye; /* Y end */ unsigned short sel_mode; /* selection mode */ }; #define TIOCL_SETSEL 2 struct tiocl { unsigned char type; unsigned char pad; struct tiocl_selection sel; }; int main() { int fd = 0; const char *dev = "/dev/char/4:1"; struct vt_consize v = {0}; struct tiocl tioc = {0}; fd = open(dev, O_RDWR, 0); v.v_rows = 3346; ioctl(fd, VT_RESIZEX, &v); tioc.type = TIOCL_SETSEL; ioctl(fd, TIOCLINUX, &tioc); return 0; } When resize the screen, update the 'vc->vc_size_row' to the new_row_size, but when 'set_origin' in 'vgacon_set_origin', vgacon use 'vga_vram_base' for 'vc_origin' and 'vc_visible_origin', not 'vc_screenbuf'. It maybe smaller than 'vc_screenbuf'. When TIOCLINUX, use the new_row_size to calc the offset, it maybe larger than the vga_vram_size in vgacon driver, then bad access. Also, if set an larger screenbuf firstly, then set an more larger screenbuf, when copy old_origin to new_origin, a bad access may happen. So, If the screen size larger than vga_vram, resize screen should be failed. This alse fix CVE-2020-8649 and CVE-2020-8647. Linus pointed out that overflow checking seems absent. We're saved by the existing bounds checks in vc_do_resize() with rather strict limits: if (cols > VC_RESIZE_MAXCOL || lines > VC_RESIZE_MAXROW) return -EINVAL; Fixes: 0aec4867dca14 ("[PATCH] SVGATextMode fix") Reference: CVE-2020-8647 and CVE-2020-8649 Reported-by: Hulk Robot <hulkci@huawei.com> Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com> [danvet: augment commit message to point out overflow safety] Cc: stable@vger.kernel.org Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: https://patchwork.freedesktop.org/patch/msgid/20200304022429.37738-1-zhangxiaoxu5@huawei.com
1 parent 2ac4853
fsl-corenet-cf.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CoreNet Coherency Fabric error reporting
*
* Copyright 2014 Freescale Semiconductor Inc.
*/
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
enum ccf_version {
CCF1,
CCF2,
};
struct ccf_info {
enum ccf_version version;
int err_reg_offs;
bool has_brr;
};
static const struct ccf_info ccf1_info = {
.version = CCF1,
.err_reg_offs = 0xa00,
.has_brr = false,
};
static const struct ccf_info ccf2_info = {
.version = CCF2,
.err_reg_offs = 0xe40,
.has_brr = true,
};
/*
* This register is present but not documented, with different values for
* IP_ID, on other chips with fsl,corenet2-cf such as t4240 and b4860.
*/
#define CCF_BRR 0xbf8
#define CCF_BRR_IPID 0xffff0000
#define CCF_BRR_IPID_T1040 0x09310000
static const struct of_device_id ccf_matches[] = {
{
.compatible = "fsl,corenet1-cf",
.data = &ccf1_info,
},
{
.compatible = "fsl,corenet2-cf",
.data = &ccf2_info,
},
{}
};
MODULE_DEVICE_TABLE(of, ccf_matches);
struct ccf_err_regs {
u32 errdet; /* 0x00 Error Detect Register */
/* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */
u32 errdis;
/* 0x08 Error Interrupt Enable Register (ccf2 only) */
u32 errinten;
u32 cecar; /* 0x0c Error Capture Attribute Register */
u32 cecaddrh; /* 0x10 Error Capture Address High */
u32 cecaddrl; /* 0x14 Error Capture Address Low */
u32 cecar2; /* 0x18 Error Capture Attribute Register 2 */
};
/* LAE/CV also valid for errdis and errinten */
#define ERRDET_LAE (1 << 0) /* Local Access Error */
#define ERRDET_CV (1 << 1) /* Coherency Violation */
#define ERRDET_UTID (1 << 2) /* Unavailable Target ID (t1040) */
#define ERRDET_MCST (1 << 3) /* Multicast Stash (t1040) */
#define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */
#define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT)
#define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */
#define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */
#define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */
#define CECAR_SRCID_SHIFT_CCF1 24
#define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1)
#define CECAR_SRCID_SHIFT_CCF2 18
#define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2)
#define CECADDRH_ADDRH 0xff
struct ccf_private {
const struct ccf_info *info;
struct device *dev;
void __iomem *regs;
struct ccf_err_regs __iomem *err_regs;
bool t1040;
};
static irqreturn_t ccf_irq(int irq, void *dev_id)
{
struct ccf_private *ccf = dev_id;
static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
u32 errdet, cecar, cecar2;
u64 addr;
u32 src_id;
bool uvt = false;
bool cap_valid = false;
errdet = ioread32be(&ccf->err_regs->errdet);
cecar = ioread32be(&ccf->err_regs->cecar);
cecar2 = ioread32be(&ccf->err_regs->cecar2);
addr = ioread32be(&ccf->err_regs->cecaddrl);
addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) &
CECADDRH_ADDRH)) << 32;
if (!__ratelimit(&ratelimit))
goto out;
switch (ccf->info->version) {
case CCF1:
if (cecar & CECAR_VAL) {
if (cecar & CECAR_UVT)
uvt = true;
src_id = (cecar & CECAR_SRCID_MASK_CCF1) >>
CECAR_SRCID_SHIFT_CCF1;
cap_valid = true;
}
break;
case CCF2:
if (errdet & ERRDET_CAP) {
src_id = (cecar & CECAR_SRCID_MASK_CCF2) >>
CECAR_SRCID_SHIFT_CCF2;
cap_valid = true;
}
break;
}
dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n",
errdet, cecar, cecar2);
if (errdet & ERRDET_LAE) {
if (uvt)
dev_crit(ccf->dev, "LAW Unavailable Target ID\n");
else
dev_crit(ccf->dev, "Local Access Window Error\n");
}
if (errdet & ERRDET_CV)
dev_crit(ccf->dev, "Coherency Violation\n");
if (errdet & ERRDET_UTID)
dev_crit(ccf->dev, "Unavailable Target ID\n");
if (errdet & ERRDET_MCST)
dev_crit(ccf->dev, "Multicast Stash\n");
if (cap_valid) {
dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n",
addr, src_id);
}
out:
iowrite32be(errdet, &ccf->err_regs->errdet);
return errdet ? IRQ_HANDLED : IRQ_NONE;
}
static int ccf_probe(struct platform_device *pdev)
{
struct ccf_private *ccf;
struct resource *r;
const struct of_device_id *match;
u32 errinten;
int ret, irq;
match = of_match_device(ccf_matches, &pdev->dev);
if (WARN_ON(!match))
return -ENODEV;
ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL);
if (!ccf)
return -ENOMEM;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "%s: no mem resource\n", __func__);
return -ENXIO;
}
ccf->regs = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(ccf->regs)) {
dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__);
return PTR_ERR(ccf->regs);
}
ccf->dev = &pdev->dev;
ccf->info = match->data;
ccf->err_regs = ccf->regs + ccf->info->err_reg_offs;
if (ccf->info->has_brr) {
u32 brr = ioread32be(ccf->regs + CCF_BRR);
if ((brr & CCF_BRR_IPID) == CCF_BRR_IPID_T1040)
ccf->t1040 = true;
}
dev_set_drvdata(&pdev->dev, ccf);
irq = platform_get_irq(pdev, 0);
if (!irq) {
dev_err(&pdev->dev, "%s: no irq\n", __func__);
return -ENXIO;
}
ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf);
if (ret) {
dev_err(&pdev->dev, "%s: can't request irq\n", __func__);
return ret;
}
errinten = ERRDET_LAE | ERRDET_CV;
if (ccf->t1040)
errinten |= ERRDET_UTID | ERRDET_MCST;
switch (ccf->info->version) {
case CCF1:
/* On CCF1 this register enables rather than disables. */
iowrite32be(errinten, &ccf->err_regs->errdis);
break;
case CCF2:
iowrite32be(0, &ccf->err_regs->errdis);
iowrite32be(errinten, &ccf->err_regs->errinten);
break;
}
return 0;
}
static int ccf_remove(struct platform_device *pdev)
{
struct ccf_private *ccf = dev_get_drvdata(&pdev->dev);
switch (ccf->info->version) {
case CCF1:
iowrite32be(0, &ccf->err_regs->errdis);
break;
case CCF2:
/*
* We clear errdis on ccf1 because that's the only way to
* disable interrupts, but on ccf2 there's no need to disable
* detection.
*/
iowrite32be(0, &ccf->err_regs->errinten);
break;
}
return 0;
}
static struct platform_driver ccf_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = ccf_matches,
},
.probe = ccf_probe,
.remove = ccf_remove,
};
module_platform_driver(ccf_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale Semiconductor");
MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting");
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...