Some touchpads (e.g. ThinkPad T14 Gen 1) have buggy firmware that reports a register descriptor structure size that is too small for the number of registers it claims to have in the presence map. The remaining bytes in the structure are 0, which with the new strict bounds checking causes the parser to fail with -EIO, aborting the device probe. Tolerate such short reads by dropping the remaining (unparseable or 0-size) registers from the list instead of failing the probe, preventing the driver from trying to use them.
Unified · Split
| @@ -616,8 +616,8 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, · 615 unmodified lines | ||
| 616 | 616 | unsigned int presence_offset; |
| 617 | 617 | unsigned int map_offset; |
| 618 | 618 | unsigned int offset; |
| 619 | + unsigned int num_registers; | |
| 619 | 620 | unsigned int reg; |
| 620 | − int i; | |
| 621 | 621 | int b; |
| 622 | 622 | int ret; |
| 623 | 623 | |
| @@ -657,7 +657,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, · 33 unmodified lines | ||
| 657 | 657 | |
| 658 | 658 | memset(presence_map, 0, sizeof(presence_map)); |
| 659 | 659 | map_offset = 0; |
| 660 | − for (i = presence_offset; i < size_presence_reg; i++) { | |
| 660 | + for (int i = presence_offset; i < size_presence_reg; i++) { | |
| 661 | 661 | for (b = 0; b < 8; b++) { |
| 662 | 662 | if (buf[i] & BIT(b)) { |
| 663 | 663 | if (map_offset >= RMI_REG_DESC_PRESENCE_BITS) |
| @@ -697,28 +697,41 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, · 33 unmodified lines | ||
| 697 | 697 | if (ret) |
| 698 | 698 | return ret; |
| 699 | 699 | |
| 700 | − reg = find_first_bit(presence_map, RMI_REG_DESC_PRESENCE_BITS); | |
| 701 | 700 | offset = 0; |
| 702 | − for (i = 0; i < rdesc->num_registers; i++) { | |
| 703 | − struct rmi_register_desc_item *item = &rdesc->registers[i]; | |
| 701 | + num_registers = 0; | |
| 702 | + for_each_set_bit(reg, presence_map, RMI_REG_DESC_PRESENCE_BITS) { | |
| 703 | + struct rmi_register_desc_item *item = &rdesc->registers[num_registers]; | |
| 704 | 704 | int item_size; |
| 705 | 705 | |
| 706 | + if (offset >= rdesc->struct_size) | |
| 707 | + break; | |
| 708 | + | |
| 706 | 709 | item_size = rmi_parse_register_desc_item(item, |
| 707 | 710 | &struct_buf[offset], |
| 708 | 711 | rdesc->struct_size - offset); |
| 709 | − if (item_size < 0) | |
| 710 | − return item_size; | |
| 712 | + if (item_size < 0) { | |
| 713 | + dev_warn(&d->dev, | |
| 714 | + "%s: Failed to parse register %d descriptor, ignoring it\n", | |
| 715 | + __func__, reg); | |
| 716 | + break; | |
| 717 | + } | |
| 711 | 718 | |
| 712 | 719 | item->reg = reg; |
| 713 | 720 | offset += item_size; |
| 714 | 721 | |
| 715 | − rmi_dbg(RMI_DEBUG_CORE, &d->dev, | |
| 716 | − "%s: reg: %d reg size: %u subpackets: %d\n", __func__, | |
| 717 | − item->reg, item->reg_size, item->num_subpackets); | |
| 722 | + if (item->reg_size == 0) { | |
| 723 | + dev_warn(&d->dev, | |
| 724 | + "%s: Register %d has 0 size, ignoring it\n", | |
| 725 | + __func__, item->reg); | |
| 726 | + } else { | |
| 727 | + rmi_dbg(RMI_DEBUG_CORE, &d->dev, | |
| 728 | + "%s: reg: %d reg size: %u subpackets: %d\n", __func__, | |
| 729 | + item->reg, item->reg_size, item->num_subpackets); | |
| 718 | 730 | |
| 719 | − reg = find_next_bit(presence_map, | |
| 720 | − RMI_REG_DESC_PRESENCE_BITS, reg + 1); | |
| 731 | + num_registers++; | |
| 732 | + } | |
| 721 | 733 | } |
| 734 | + rdesc->num_registers = num_registers; | |
| 722 | 735 | |
| 723 | 736 | return 0; |
| 724 | 737 | } |