HID: playstation: sanity check DualShock4 calibration data.
authorRoderick Colenbrander <roderick@gaikai.com>
Fri, 6 Jan 2023 01:59:07 +0000 (17:59 -0800)
committerJiri Kosina <jkosina@suse.cz>
Wed, 18 Jan 2023 09:10:27 +0000 (10:10 +0100)
Some DualShock4 devices report invalid calibration data resulting
in kernel oopses due to division by zero during report handling.

The devices affected generally appear to be clone devices, which don't
implement all reports properly and don't populate proper calibration
data. The issue may have been seen on an official device with erased
calibration reports.

This patch prevents the crashes by essentially disabling calibration
when invalid values are detected.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Tested-by: Alain Carlucci <alain.carlucci@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
drivers/hid/hid-playstation.c

index f399bf0d3c8cc33d117d0b060960e611619acc3c..1eba50b45cb1e09e823481e08a62a8c96b631f6d 100644 (file)
@@ -1737,6 +1737,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
        int speed_2x;
        int range_2g;
        int ret = 0;
+       int i;
        uint8_t *buf;
 
        if (ds4->base.hdev->bus == BUS_USB) {
@@ -1830,6 +1831,21 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
        ds4->gyro_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S;
        ds4->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus;
 
+       /*
+        * Sanity check gyro calibration data. This is needed to prevent crashes
+        * during report handling of virtual, clone or broken devices not implementing
+        * calibration data properly.
+        */
+       for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) {
+               if (ds4->gyro_calib_data[i].sens_denom == 0) {
+                       hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.",
+                                       ds4->gyro_calib_data[i].abs_code);
+                       ds4->gyro_calib_data[i].bias = 0;
+                       ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE;
+                       ds4->gyro_calib_data[i].sens_denom = S16_MAX;
+               }
+       }
+
        /*
         * Set accelerometer calibration and normalization parameters.
         * Data values will be normalized to 1/DS4_ACC_RES_PER_G g.
@@ -1852,6 +1868,21 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
        ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G;
        ds4->accel_calib_data[2].sens_denom = range_2g;
 
+       /*
+        * Sanity check accelerometer calibration data. This is needed to prevent crashes
+        * during report handling of virtual, clone or broken devices not implementing calibration
+        * data properly.
+        */
+       for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) {
+               if (ds4->accel_calib_data[i].sens_denom == 0) {
+                       hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.",
+                                       ds4->accel_calib_data[i].abs_code);
+                       ds4->accel_calib_data[i].bias = 0;
+                       ds4->accel_calib_data[i].sens_numer = DS4_ACC_RANGE;
+                       ds4->accel_calib_data[i].sens_denom = S16_MAX;
+               }
+       }
+
 err_free:
        kfree(buf);
        return ret;