--- a/sys/bus/isa/isa_common.c +++ b/sys/bus/isa/isa_common.c @@ -75,6 +75,13 @@ MALLOC_DEFINE(M_ISADEV, "isadev", "ISA device"); +/* + * Cap on candidate addresses probed per resource range in isa_find_*. + * A crafted PnP descriptor with a huge window and align=1 could otherwise + * spin the allocator ~2^32 times (boot-time stall). + */ +#define ISA_FIND_MAX_TRIES 65536 + static devclass_t isa_devclass; static int isa_running; @@ -127,11 +134,13 @@ result->ic_nmem = config->ic_nmem; for (i = 0; i < config->ic_nmem; i++) { u_int32_t start, end, size, align; + int tries = 0; for (start = config->ic_mem[i].ir_start, end = config->ic_mem[i].ir_end, size = config->ic_mem[i].ir_size, align = config->ic_mem[i].ir_align; - start + size - 1 <= end; + start <= end && start + size - 1 <= end + && ++tries <= ISA_FIND_MAX_TRIES; start += MAX(align, 1)) { bus_set_resource(child, SYS_RES_MEMORY, i, start, size, -1); @@ -942,6 +951,23 @@ struct isa_device* idev = DEVTOISA(child); struct isa_config_entry *newice, *ice; + /* + * Security: the count fields index fixed-size arrays in every + * consumer (isa_find_* loops, config callbacks). Reject any + * config with out-of-range counts instead of persisting it. + */ + if (config->ic_nmem < 0 || config->ic_nmem > ISA_NMEM + || config->ic_nport < 0 || config->ic_nport > ISA_NPORT + || config->ic_nirq < 0 || config->ic_nirq > ISA_NIRQ + || config->ic_ndrq < 0 || config->ic_ndrq > ISA_NDRQ) { + kprintf("isa_add_config: %s%d bogus counts " + "(nmem=%d nport=%d nirq=%d ndrq=%d), rejected\n", + device_get_name(child), device_get_unit(child), + config->ic_nmem, config->ic_nport, + config->ic_nirq, config->ic_ndrq); + return EINVAL; + } + newice = kmalloc(sizeof *ice, M_DEVBUF, M_WAITOK); newice->ice_priority = priority; newice->ice_config = *config; --- a/sys/bus/isa/pnp.c +++ b/sys/bus/isa/pnp.c @@ -335,13 +335,13 @@ case PNP_QUIRK_EXTRA_IO: if (config == NULL) break; - if (qp->arg1 != 0) { + if (qp->arg1 != 0 && config->ic_nport < ISA_NPORT) { config->ic_nport++; config->ic_port[config->ic_nport - 1] = config->ic_port[0]; config->ic_port[config->ic_nport - 1].ir_start += qp->arg1; config->ic_port[config->ic_nport - 1].ir_end += qp->arg1; } - if (qp->arg2 != 0) { + if (qp->arg2 != 0 && config->ic_nport < ISA_NPORT) { config->ic_nport++; config->ic_port[config->ic_nport - 1] = config->ic_port[0]; config->ic_port[config->ic_nport - 1].ir_start += qp->arg2; --- a/sys/bus/isa/pnpparse.c +++ b/sys/bus/isa/pnpparse.c @@ -98,7 +98,7 @@ break; case PNP_TAG_IRQ_FORMAT: - if (config->ic_nirq == ISA_NIRQ) { + if (config->ic_nirq >= ISA_NIRQ) { pnp_printf(id, "too many irqs\n"); return (1); } @@ -116,7 +116,7 @@ break; case PNP_TAG_DMA_FORMAT: - if (config->ic_ndrq == ISA_NDRQ) { + if (config->ic_ndrq >= ISA_NDRQ) { pnp_printf(id, "too many drqs\n"); return (1); } @@ -134,7 +134,7 @@ break; case PNP_TAG_IO_RANGE: - if (config->ic_nport == ISA_NPORT) { + if (config->ic_nport >= ISA_NPORT) { pnp_printf(id, "too many ports\n"); return (1); } @@ -171,7 +171,7 @@ break; case PNP_TAG_IO_FIXED: - if (config->ic_nport == ISA_NPORT) { + if (config->ic_nport >= ISA_NPORT) { pnp_printf(id, "too many ports\n"); return (1); } @@ -235,7 +235,7 @@ break; case PNP_TAG_MEMORY_RANGE: - if (config->ic_nmem == ISA_NMEM) { + if (config->ic_nmem >= ISA_NMEM) { pnp_printf(id, "too many memory ranges\n"); return (1); } @@ -271,7 +271,7 @@ break; case PNP_TAG_MEMORY32_RANGE: - if (config->ic_nmem == ISA_NMEM) { + if (config->ic_nmem >= ISA_NMEM) { pnp_printf(id, "too many memory ranges\n"); return (1); } @@ -301,7 +301,7 @@ break; case PNP_TAG_MEMORY32_FIXED: - if (config->ic_nmem == ISA_NMEM) { + if (config->ic_nmem >= ISA_NMEM) { pnp_printf(id, "too many memory ranges\n"); return (1); } @@ -363,7 +363,7 @@ parent = device_get_parent(dev); for (i = 0; i < from->ic_nmem; i++) { - if (to->ic_nmem == ISA_NMEM) { + if (to->ic_nmem >= ISA_NMEM) { device_printf(parent, "too many memory ranges\n"); return; } @@ -371,7 +371,7 @@ to->ic_nmem++; } for (i = 0; i < from->ic_nport; i++) { - if (to->ic_nport == ISA_NPORT) { + if (to->ic_nport >= ISA_NPORT) { device_printf(parent, "too many port ranges\n"); return; } @@ -379,7 +379,7 @@ to->ic_nport++; } for (i = 0; i < from->ic_nirq; i++) { - if (to->ic_nirq == ISA_NIRQ) { + if (to->ic_nirq >= ISA_NIRQ) { device_printf(parent, "too many irq ranges\n"); return; } @@ -387,7 +387,7 @@ to->ic_nirq++; } for (i = 0; i < from->ic_ndrq; i++) { - if (to->ic_ndrq == ISA_NDRQ) { + if (to->ic_ndrq >= ISA_NDRQ) { device_printf(parent, "too many drq ranges\n"); return; }