 57bdec5c46
			
		
	
	
		57bdec5c46
		
	
	
	
	
		
			
			When 'j = icu->nr_sense – 1', the 'j < icu->nr_sense' condition is true,
then 'j = icu->nr_sense', the'icu->init_sense[j]' has out-of-bounds access.
The asan showed stack:
ERROR: AddressSanitizer: heap-buffer-overflow on address 0x604000004d7d at pc 0x55852cd26a76 bp 0x7ffe39f26200 sp 0x7ffe39f261f0
READ of size 1 at 0x604000004d7d thread T0
    #0 0x55852cd26a75 in rxicu_realize ../hw/intc/rx_icu.c:311
    #1 0x55852cf075f7 in device_set_realized ../hw/core/qdev.c:886
    #2 0x55852cd4a32f in property_set_bool ../qom/object.c:2251
    #3 0x55852cd4f9bb in object_property_set ../qom/object.c:1398
    #4 0x55852cd54f3f in object_property_set_qobject ../qom/qom-qobject.c:28
    #5 0x55852cd4fc3f in object_property_set_bool ../qom/object.c:1465
    #6 0x55852cbf0b27 in register_icu ../hw/rx/rx62n.c:156
    #7 0x55852cbf12a6 in rx62n_realize ../hw/rx/rx62n.c:261
    #8 0x55852cf075f7 in device_set_realized ../hw/core/qdev.c:886
    #9 0x55852cd4a32f in property_set_bool ../qom/object.c:2251
    #10 0x55852cd4f9bb in object_property_set ../qom/object.c:1398
    #11 0x55852cd54f3f in object_property_set_qobject ../qom/qom-qobject.c:28
    #12 0x55852cd4fc3f in object_property_set_bool ../qom/object.c:1465
    #13 0x55852cbf1a85 in rx_gdbsim_init ../hw/rx/rx-gdbsim.c:109
    #14 0x55852cd22de0 in qemu_init ../softmmu/vl.c:4380
    #15 0x55852ca57088 in main ../softmmu/main.c:49
    #16 0x7feefafa5d42 in __libc_start_main (/lib64/libc.so.6+0x26d42)
Add the 'ice->src[i].sense' initialize to the default value, and then
process init_sense array to identify which irqs should be level-triggered.
Suggested-by: Peter Maydell <peter.maydell@linaro.org>
Reported-by: Euler Robot <euler.robot@huawei.com>
Signed-off-by: Chen Qun <kuhn.chenqun@huawei.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20201111141733.2358800-1-kuhn.chenqun@huawei.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
		
	
			
		
			
				
	
	
		
			396 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * RX Interrupt Control Unit
 | |
|  *
 | |
|  * Warning: Only ICUa is supported.
 | |
|  *
 | |
|  * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
 | |
|  *            (Rev.1.40 R01UH0033EJ0140)
 | |
|  *
 | |
|  * Copyright (c) 2019 Yoshinori Sato
 | |
|  *
 | |
|  * SPDX-License-Identifier: GPL-2.0-or-later
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it
 | |
|  * under the terms and conditions of the GNU General Public License,
 | |
|  * version 2 or later, as published by the Free Software Foundation.
 | |
|  *
 | |
|  * This program is distributed in the hope it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 | |
|  * more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/log.h"
 | |
| #include "qemu/error-report.h"
 | |
| #include "hw/irq.h"
 | |
| #include "hw/registerfields.h"
 | |
| #include "hw/qdev-properties.h"
 | |
| #include "hw/intc/rx_icu.h"
 | |
| #include "migration/vmstate.h"
 | |
| 
 | |
| REG8(IR, 0)
 | |
|   FIELD(IR, IR,  0, 1)
 | |
| REG8(DTCER, 0x100)
 | |
|   FIELD(DTCER, DTCE,  0, 1)
 | |
| REG8(IER, 0x200)
 | |
| REG8(SWINTR, 0x2e0)
 | |
|   FIELD(SWINTR, SWINT, 0, 1)
 | |
| REG16(FIR, 0x2f0)
 | |
|   FIELD(FIR, FVCT, 0,  8)
 | |
|   FIELD(FIR, FIEN, 15, 1)
 | |
| REG8(IPR, 0x300)
 | |
|   FIELD(IPR, IPR, 0, 4)
 | |
| REG8(DMRSR, 0x400)
 | |
| REG8(IRQCR, 0x500)
 | |
|   FIELD(IRQCR, IRQMD, 2, 2)
 | |
| REG8(NMISR, 0x580)
 | |
|   FIELD(NMISR, NMIST, 0, 1)
 | |
|   FIELD(NMISR, LVDST, 1, 1)
 | |
|   FIELD(NMISR, OSTST, 2, 1)
 | |
| REG8(NMIER, 0x581)
 | |
|   FIELD(NMIER, NMIEN, 0, 1)
 | |
|   FIELD(NMIER, LVDEN, 1, 1)
 | |
|   FIELD(NMIER, OSTEN, 2, 1)
 | |
| REG8(NMICLR, 0x582)
 | |
|   FIELD(NMICLR, NMICLR, 0, 1)
 | |
|   FIELD(NMICLR, OSTCLR, 2, 1)
 | |
| REG8(NMICR, 0x583)
 | |
|   FIELD(NMICR, NMIMD, 3, 1)
 | |
| 
 | |
| static void set_irq(RXICUState *icu, int n_IRQ, int req)
 | |
| {
 | |
|     if ((icu->fir & R_FIR_FIEN_MASK) &&
 | |
|         (icu->fir & R_FIR_FVCT_MASK) == n_IRQ) {
 | |
|         qemu_set_irq(icu->_fir, req);
 | |
|     } else {
 | |
|         qemu_set_irq(icu->_irq, req);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint16_t rxicu_level(RXICUState *icu, unsigned n)
 | |
| {
 | |
|     return (icu->ipr[icu->map[n]] << 8) | n;
 | |
| }
 | |
| 
 | |
| static void rxicu_request(RXICUState *icu, int n_IRQ)
 | |
| {
 | |
|     int enable;
 | |
| 
 | |
|     enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7));
 | |
|     if (n_IRQ > 0 && enable != 0 && qatomic_read(&icu->req_irq) < 0) {
 | |
|         qatomic_set(&icu->req_irq, n_IRQ);
 | |
|         set_irq(icu, n_IRQ, rxicu_level(icu, n_IRQ));
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void rxicu_set_irq(void *opaque, int n_IRQ, int level)
 | |
| {
 | |
|     RXICUState *icu = opaque;
 | |
|     struct IRQSource *src;
 | |
|     int issue;
 | |
| 
 | |
|     if (n_IRQ >= NR_IRQS) {
 | |
|         error_report("%s: IRQ %d out of range", __func__, n_IRQ);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     src = &icu->src[n_IRQ];
 | |
| 
 | |
|     level = (level != 0);
 | |
|     switch (src->sense) {
 | |
|     case TRG_LEVEL:
 | |
|         /* level-sensitive irq */
 | |
|         issue = level;
 | |
|         src->level = level;
 | |
|         break;
 | |
|     case TRG_NEDGE:
 | |
|         issue = (level == 0 && src->level == 1);
 | |
|         src->level = level;
 | |
|         break;
 | |
|     case TRG_PEDGE:
 | |
|         issue = (level == 1 && src->level == 0);
 | |
|         src->level = level;
 | |
|         break;
 | |
|     case TRG_BEDGE:
 | |
|         issue = ((level ^ src->level) & 1);
 | |
|         src->level = level;
 | |
|         break;
 | |
|     default:
 | |
|         g_assert_not_reached();
 | |
|     }
 | |
|     if (issue == 0 && src->sense == TRG_LEVEL) {
 | |
|         icu->ir[n_IRQ] = 0;
 | |
|         if (qatomic_read(&icu->req_irq) == n_IRQ) {
 | |
|             /* clear request */
 | |
|             set_irq(icu, n_IRQ, 0);
 | |
|             qatomic_set(&icu->req_irq, -1);
 | |
|         }
 | |
|         return;
 | |
|     }
 | |
|     if (issue) {
 | |
|         icu->ir[n_IRQ] = 1;
 | |
|         rxicu_request(icu, n_IRQ);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void rxicu_ack_irq(void *opaque, int no, int level)
 | |
| {
 | |
|     RXICUState *icu = opaque;
 | |
|     int i;
 | |
|     int n_IRQ;
 | |
|     int max_pri;
 | |
| 
 | |
|     n_IRQ = qatomic_read(&icu->req_irq);
 | |
|     if (n_IRQ < 0) {
 | |
|         return;
 | |
|     }
 | |
|     qatomic_set(&icu->req_irq, -1);
 | |
|     if (icu->src[n_IRQ].sense != TRG_LEVEL) {
 | |
|         icu->ir[n_IRQ] = 0;
 | |
|     }
 | |
| 
 | |
|     max_pri = 0;
 | |
|     n_IRQ = -1;
 | |
|     for (i = 0; i < NR_IRQS; i++) {
 | |
|         if (icu->ir[i]) {
 | |
|             if (max_pri < icu->ipr[icu->map[i]]) {
 | |
|                 n_IRQ = i;
 | |
|                 max_pri = icu->ipr[icu->map[i]];
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (n_IRQ >= 0) {
 | |
|         rxicu_request(icu, n_IRQ);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size)
 | |
| {
 | |
|     RXICUState *icu = opaque;
 | |
|     int reg = addr & 0xff;
 | |
| 
 | |
|     if ((addr != A_FIR && size != 1) ||
 | |
|         (addr == A_FIR && size != 2)) {
 | |
|         qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid read size 0x%"
 | |
|                                        HWADDR_PRIX "\n",
 | |
|                       addr);
 | |
|         return UINT64_MAX;
 | |
|     }
 | |
|     switch (addr) {
 | |
|     case A_IR ... A_IR + 0xff:
 | |
|         return icu->ir[reg] & R_IR_IR_MASK;
 | |
|     case A_DTCER ... A_DTCER + 0xff:
 | |
|         return icu->dtcer[reg] & R_DTCER_DTCE_MASK;
 | |
|     case A_IER ... A_IER + 0x1f:
 | |
|         return icu->ier[reg];
 | |
|     case A_SWINTR:
 | |
|         return 0;
 | |
|     case A_FIR:
 | |
|         return icu->fir & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
 | |
|     case A_IPR ... A_IPR + 0x8f:
 | |
|         return icu->ipr[reg] & R_IPR_IPR_MASK;
 | |
|     case A_DMRSR:
 | |
|     case A_DMRSR + 4:
 | |
|     case A_DMRSR + 8:
 | |
|     case A_DMRSR + 12:
 | |
|         return icu->dmasr[reg >> 2];
 | |
|     case A_IRQCR ... A_IRQCR + 0x1f:
 | |
|         return icu->src[64 + reg].sense << R_IRQCR_IRQMD_SHIFT;
 | |
|     case A_NMISR:
 | |
|     case A_NMICLR:
 | |
|         return 0;
 | |
|     case A_NMIER:
 | |
|         return icu->nmier;
 | |
|     case A_NMICR:
 | |
|         return icu->nmicr;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
 | |
|                                  "not implemented.\n",
 | |
|                       addr);
 | |
|         break;
 | |
|     }
 | |
|     return UINT64_MAX;
 | |
| }
 | |
| 
 | |
| static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
 | |
| {
 | |
|     RXICUState *icu = opaque;
 | |
|     int reg = addr & 0xff;
 | |
| 
 | |
|     if ((addr != A_FIR && size != 1) ||
 | |
|         (addr == A_FIR && size != 2)) {
 | |
|         qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid write size at "
 | |
|                                        "0x%" HWADDR_PRIX "\n",
 | |
|                       addr);
 | |
|         return;
 | |
|     }
 | |
|     switch (addr) {
 | |
|     case A_IR ... A_IR + 0xff:
 | |
|         if (icu->src[reg].sense != TRG_LEVEL && val == 0) {
 | |
|             icu->ir[reg] = 0;
 | |
|         }
 | |
|         break;
 | |
|     case A_DTCER ... A_DTCER + 0xff:
 | |
|         icu->dtcer[reg] = val & R_DTCER_DTCE_MASK;
 | |
|         qemu_log_mask(LOG_UNIMP, "rx_icu: DTC not implemented\n");
 | |
|         break;
 | |
|     case A_IER ... A_IER + 0x1f:
 | |
|         icu->ier[reg] = val;
 | |
|         break;
 | |
|     case A_SWINTR:
 | |
|         if (val & R_SWINTR_SWINT_MASK) {
 | |
|             qemu_irq_pulse(icu->_swi);
 | |
|         }
 | |
|         break;
 | |
|     case A_FIR:
 | |
|         icu->fir = val & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
 | |
|         break;
 | |
|     case A_IPR ... A_IPR + 0x8f:
 | |
|         icu->ipr[reg] = val & R_IPR_IPR_MASK;
 | |
|         break;
 | |
|     case A_DMRSR:
 | |
|     case A_DMRSR + 4:
 | |
|     case A_DMRSR + 8:
 | |
|     case A_DMRSR + 12:
 | |
|         icu->dmasr[reg >> 2] = val;
 | |
|         qemu_log_mask(LOG_UNIMP, "rx_icu: DMAC not implemented\n");
 | |
|         break;
 | |
|     case A_IRQCR ... A_IRQCR + 0x1f:
 | |
|         icu->src[64 + reg].sense = val >> R_IRQCR_IRQMD_SHIFT;
 | |
|         break;
 | |
|     case A_NMICLR:
 | |
|         break;
 | |
|     case A_NMIER:
 | |
|         icu->nmier |= val & (R_NMIER_NMIEN_MASK |
 | |
|                              R_NMIER_LVDEN_MASK |
 | |
|                              R_NMIER_OSTEN_MASK);
 | |
|         break;
 | |
|     case A_NMICR:
 | |
|         if ((icu->nmier & R_NMIER_NMIEN_MASK) == 0) {
 | |
|             icu->nmicr = val & R_NMICR_NMIMD_MASK;
 | |
|         }
 | |
|         break;
 | |
|     default:
 | |
|         qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
 | |
|                                  "not implemented\n",
 | |
|                       addr);
 | |
|         break;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps icu_ops = {
 | |
|     .write = icu_write,
 | |
|     .read  = icu_read,
 | |
|     .endianness = DEVICE_LITTLE_ENDIAN,
 | |
|     .impl = {
 | |
|         .min_access_size = 1,
 | |
|         .max_access_size = 2,
 | |
|     },
 | |
|     .valid = {
 | |
|         .min_access_size = 1,
 | |
|         .max_access_size = 2,
 | |
|     },
 | |
| };
 | |
| 
 | |
| static void rxicu_realize(DeviceState *dev, Error **errp)
 | |
| {
 | |
|     RXICUState *icu = RX_ICU(dev);
 | |
|     int i;
 | |
| 
 | |
|     if (icu->init_sense == NULL) {
 | |
|         qemu_log_mask(LOG_GUEST_ERROR,
 | |
|                       "rx_icu: trigger-level property must be set.");
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < NR_IRQS; i++) {
 | |
|         icu->src[i].sense = TRG_PEDGE;
 | |
|     }
 | |
|     for (i = 0; i < icu->nr_sense; i++) {
 | |
|         uint8_t irqno = icu->init_sense[i];
 | |
|         icu->src[irqno].sense = TRG_LEVEL;
 | |
|     }
 | |
|     icu->req_irq = -1;
 | |
| }
 | |
| 
 | |
| static void rxicu_init(Object *obj)
 | |
| {
 | |
|     SysBusDevice *d = SYS_BUS_DEVICE(obj);
 | |
|     RXICUState *icu = RX_ICU(obj);
 | |
| 
 | |
|     memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops,
 | |
|                           icu, "rx-icu", 0x600);
 | |
|     sysbus_init_mmio(d, &icu->memory);
 | |
| 
 | |
|     qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, NR_IRQS);
 | |
|     qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1);
 | |
|     sysbus_init_irq(d, &icu->_irq);
 | |
|     sysbus_init_irq(d, &icu->_fir);
 | |
|     sysbus_init_irq(d, &icu->_swi);
 | |
| }
 | |
| 
 | |
| static void rxicu_fini(Object *obj)
 | |
| {
 | |
|     RXICUState *icu = RX_ICU(obj);
 | |
|     g_free(icu->map);
 | |
|     g_free(icu->init_sense);
 | |
| }
 | |
| 
 | |
| static const VMStateDescription vmstate_rxicu = {
 | |
|     .name = "rx-icu",
 | |
|     .version_id = 1,
 | |
|     .minimum_version_id = 1,
 | |
|     .fields = (VMStateField[]) {
 | |
|         VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS),
 | |
|         VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS),
 | |
|         VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8),
 | |
|         VMSTATE_UINT8_ARRAY(ipr, RXICUState, 142),
 | |
|         VMSTATE_UINT8_ARRAY(dmasr, RXICUState, 4),
 | |
|         VMSTATE_UINT16(fir, RXICUState),
 | |
|         VMSTATE_UINT8(nmisr, RXICUState),
 | |
|         VMSTATE_UINT8(nmier, RXICUState),
 | |
|         VMSTATE_UINT8(nmiclr, RXICUState),
 | |
|         VMSTATE_UINT8(nmicr, RXICUState),
 | |
|         VMSTATE_INT16(req_irq, RXICUState),
 | |
|         VMSTATE_END_OF_LIST()
 | |
|     }
 | |
| };
 | |
| 
 | |
| static Property rxicu_properties[] = {
 | |
|     DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map,
 | |
|                       qdev_prop_uint8, uint8_t),
 | |
|     DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense,
 | |
|                       qdev_prop_uint8, uint8_t),
 | |
|     DEFINE_PROP_END_OF_LIST(),
 | |
| };
 | |
| 
 | |
| static void rxicu_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
| 
 | |
|     dc->realize = rxicu_realize;
 | |
|     dc->vmsd = &vmstate_rxicu;
 | |
|     device_class_set_props(dc, rxicu_properties);
 | |
| }
 | |
| 
 | |
| static const TypeInfo rxicu_info = {
 | |
|     .name = TYPE_RX_ICU,
 | |
|     .parent = TYPE_SYS_BUS_DEVICE,
 | |
|     .instance_size = sizeof(RXICUState),
 | |
|     .instance_init = rxicu_init,
 | |
|     .instance_finalize = rxicu_fini,
 | |
|     .class_init = rxicu_class_init,
 | |
| };
 | |
| 
 | |
| static void rxicu_register_types(void)
 | |
| {
 | |
|     type_register_static(&rxicu_info);
 | |
| }
 | |
| 
 | |
| type_init(rxicu_register_types)
 |