History

Input: elan_i2c - prevent division by zero and arithmetic underflow

The Elan I2C touchpad driver queries the device for its physical dimensions and trace counts to calculate the device resolution and width. However, if the device firmware or device tree provides invalid zero values for x_traces or y_traces, it results in a fatal division-by-zero exception leading to a kernel panic during device probe. Add checks to ensure these parameters are non-zero before performing the division. If invalid trace values are detected, fall back to a safe default of 1. Additionally, prevent an arithmetic underflow in the touch reporting logic. Previously, if the calculated or fallback width was smaller than ETP_FWIDTH_REDUCE (90), the subtraction would underflow, resulting in a massive unsigned integer being reported to userspace. Clamp the adjusted width to a minimum of 0 to safely handle small physical dimensions and fallback scenarios. Completing the probe with safe fallback values ensures the sysfs nodes are created, keeping the firmware update path intact so a recovery firmware can be flashed to the device.

Ranjan Kumar committed 4 months ago

Commit df2b8181 parent ef166ce1 file · +30 −6Browse files

Showing the changes to drivers/input (1 of 1 file). Show all

Unified · Split

drivers/input/mouse/elan_i2c_core.c+30−6
@@ -428,8 +428,17 @@ static int elan_query_device_parameters(struct elan_tp_data *data) · 427 unmodified lines
428 if (error)428 if (error)
429 return error;429 return error;
430 }430 }
431 data->width_x = data->max_x / x_traces;431
432 data->width_y = data->max_y / y_traces;432 if (!x_traces || !y_traces) {
433 dev_warn(&client->dev,
434 "invalid trace numbers: x=%u, y=%u\n",
435 x_traces, y_traces);
436 data->width_x = 1;
437 data->width_y = 1;
438 } else {
439 data->width_x = data->max_x / x_traces;
440 data->width_y = data->max_y / y_traces;
441 }
433 442
434 if (device_property_read_u32(&client->dev,443 if (device_property_read_u32(&client->dev,
435 "touchscreen-x-mm", &x_mm) ||444 "touchscreen-x-mm", &x_mm) ||
@@ -443,8 +452,16 @@ static int elan_query_device_parameters(struct elan_tp_data *data) · 7 unmodified lines
443 data->x_res = elan_convert_resolution(hw_x_res, data->pattern);452 data->x_res = elan_convert_resolution(hw_x_res, data->pattern);
444 data->y_res = elan_convert_resolution(hw_y_res, data->pattern);453 data->y_res = elan_convert_resolution(hw_y_res, data->pattern);
445 } else {454 } else {
446 data->x_res = (data->max_x + 1) / x_mm;455 if (unlikely(x_mm == 0 || y_mm == 0)) {
447 data->y_res = (data->max_y + 1) / y_mm;456 dev_warn(&client->dev,
457 "invalid physical dimensions: x_mm=%u, y_mm=%u\n",
458 x_mm, y_mm);
459 data->x_res = 1;
460 data->y_res = 1;
461 } else {
462 data->x_res = (data->max_x + 1) / x_mm;
463 data->y_res = (data->max_y + 1) / y_mm;
464 }
448 }465 }
449 466
450 if (device_property_read_bool(&client->dev, "elan,clickpad"))467 if (device_property_read_bool(&client->dev, "elan,clickpad"))
@@ -956,6 +973,7 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, · 505 unmodified lines
956 973
957 if (data->report_features & ETP_FEATURE_REPORT_MK) {974 if (data->report_features & ETP_FEATURE_REPORT_MK) {
958 unsigned int mk_x, mk_y, area_x, area_y;975 unsigned int mk_x, mk_y, area_x, area_y;
976 int adj_width_x, adj_width_y;
959 u8 mk_data = high_precision ?977 u8 mk_data = high_precision ?
960 packet[ETP_MK_DATA_OFFSET + contact_num] :978 packet[ETP_MK_DATA_OFFSET + contact_num] :
961 finger_data[3];979 finger_data[3];
@@ -967,8 +985,14 @@ static void elan_report_contact(struct elan_tp_data *data, int contact_num, · 5 unmodified lines
967 * To avoid treating large finger as palm, let's reduce985 * To avoid treating large finger as palm, let's reduce
968 * the width x and y per trace.986 * the width x and y per trace.
969 */987 */
970 area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);988
971 area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);989 adj_width_x = data->width_x > ETP_FWIDTH_REDUCE ?
990 data->width_x - ETP_FWIDTH_REDUCE : 0;
991 adj_width_y = data->width_y > ETP_FWIDTH_REDUCE ?
992 data->width_y - ETP_FWIDTH_REDUCE : 0;
993
994 area_x = mk_x * adj_width_x;
995 area_y = mk_y * adj_width_y;
972 996
973 input_report_abs(input, ABS_TOOL_WIDTH, mk_x);997 input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
974 input_report_abs(input, ABS_MT_TOUCH_MAJOR,998 input_report_abs(input, ABS_MT_TOUCH_MAJOR,