mirror of
https://github.com/S3NEO/android_kernel_samsung_msm8226.git
synced 2024-11-07 03:47:13 +00:00
HID: Fix assumption that devices have inputs
commit d9d4b1e46d9543a82c23f6df03f4ad697dab361b upstream. The syzbot fuzzer found a slab-out-of-bounds write bug in the hid-gaff driver. The problem is caused by the driver's assumption that the device must have an input report. While this will be true for all normal HID input devices, a suitably malicious device can violate the assumption. The same assumption is present in over a dozen other HID drivers. This patch fixes them by checking that the list of hid_inputs for the hid_device is nonempty before allowing it to be used. Reported-and-tested-by: syzbot+403741a091bf41d4ae79@syzkaller.appspotmail.com Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> [bwh: Backported to 3.16: - Drop changes in hid-logitech-hidpp, hid-microsoft - Adjust context] Signed-off-by: Ben Hutchings <ben@decadent.org.uk> [haggertk: Backported to android/3.4: - Drop changes to hid-sony, add changes to hid-pidff] CVE-2019-19532 Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org> Change-Id: Icfe325236f0c40aa0c3ca638e903179b3935ad1e
This commit is contained in:
parent
c3a49f0f62
commit
f9cc3d1366
12 changed files with 108 additions and 32 deletions
|
@ -77,13 +77,20 @@ static int axff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct axff_device *axff;
|
struct axff_device *axff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
struct list_head *report_list =&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int field_count = 0;
|
int field_count = 0;
|
||||||
int i, j;
|
int i, j;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -89,13 +89,19 @@ static int drff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct drff_device *drff;
|
struct drff_device *drff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -61,13 +61,19 @@ static int emsff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct emsff_device *emsff;
|
struct emsff_device *emsff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_first_entry(&hid->inputs,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_first_entry(&hid->inputs, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -79,14 +79,20 @@ static int gaff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct gaff_device *gaff;
|
struct gaff_device *gaff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct list_head *report_ptr = report_list;
|
struct list_head *report_ptr = report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output reports found\n");
|
hid_err(hid, "no output reports found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -143,13 +143,19 @@ static int holtekff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct holtekff_device *holtekff;
|
struct holtekff_device *holtekff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
|
||||||
struct list_head *report_list =
|
struct list_head *report_list =
|
||||||
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
if (list_empty(report_list)) {
|
if (list_empty(report_list)) {
|
||||||
hid_err(hid, "no output report found\n");
|
hid_err(hid, "no output report found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -64,11 +64,17 @@ int lg2ff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct lg2ff_device *lg2ff;
|
struct lg2ff_device *lg2ff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *dev;
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
|
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7);
|
||||||
if (!report)
|
if (!report)
|
||||||
|
|
|
@ -131,12 +131,19 @@ static const signed short ff3_joystick_ac[] = {
|
||||||
|
|
||||||
int lg3ff_init(struct hid_device *hid)
|
int lg3ff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
const signed short *ff_bits = ff3_joystick_ac;
|
const signed short *ff_bits = ff3_joystick_ac;
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -338,13 +338,20 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
|
||||||
|
|
||||||
int lg4ff_init(struct hid_device *hid)
|
int lg4ff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
struct lg4ff_device_entry *entry;
|
struct lg4ff_device_entry *entry;
|
||||||
struct usb_device_descriptor *udesc;
|
struct usb_device_descriptor *udesc;
|
||||||
int error, i, j;
|
int error, i, j;
|
||||||
__u16 bcdDevice, rev_maj, rev_min;
|
__u16 bcdDevice, rev_maj, rev_min;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -129,12 +129,19 @@ static void hid_lgff_set_autocenter(struct input_dev *dev, u16 magnitude)
|
||||||
|
|
||||||
int lgff_init(struct hid_device* hid)
|
int lgff_init(struct hid_device* hid)
|
||||||
{
|
{
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
struct hid_input *hidinput;
|
||||||
struct input_dev *dev = hidinput->input;
|
struct input_dev *dev;
|
||||||
const signed short *ff_bits = ff_joystick;
|
const signed short *ff_bits = ff_joystick;
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
/* Check that the report looks ok */
|
/* Check that the report looks ok */
|
||||||
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
|
@ -128,12 +128,18 @@ static int tmff_init(struct hid_device *hid, const signed short *ff_bits)
|
||||||
struct tmff_device *tmff;
|
struct tmff_device *tmff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct list_head *report_list;
|
struct list_head *report_list;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *input_dev;
|
||||||
struct input_dev *input_dev = hidinput->input;
|
|
||||||
int error;
|
int error;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
input_dev = hidinput->input;
|
||||||
|
|
||||||
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
|
||||||
if (!tmff)
|
if (!tmff)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
@ -68,11 +68,17 @@ static int zpff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct zpff_device *zpff;
|
struct zpff_device *zpff;
|
||||||
struct hid_report *report;
|
struct hid_report *report;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *dev;
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
int i, error;
|
int i, error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
|
report = hid_validate_values(hid, HID_OUTPUT_REPORT, 0, i, 1);
|
||||||
if (!report)
|
if (!report)
|
||||||
|
|
|
@ -1232,13 +1232,19 @@ static int pidff_check_autocenter(struct pidff_device *pidff,
|
||||||
int hid_pidff_init(struct hid_device *hid)
|
int hid_pidff_init(struct hid_device *hid)
|
||||||
{
|
{
|
||||||
struct pidff_device *pidff;
|
struct pidff_device *pidff;
|
||||||
struct hid_input *hidinput = list_entry(hid->inputs.next,
|
struct hid_input *hidinput;
|
||||||
struct hid_input, list);
|
struct input_dev *dev;
|
||||||
struct input_dev *dev = hidinput->input;
|
|
||||||
struct ff_device *ff;
|
struct ff_device *ff;
|
||||||
int max_effects;
|
int max_effects;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (list_empty(&hid->inputs)) {
|
||||||
|
hid_err(hid, "no inputs found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
hidinput = list_entry(hid->inputs.next, struct hid_input, list);
|
||||||
|
dev = hidinput->input;
|
||||||
|
|
||||||
hid_dbg(hid, "starting pid init\n");
|
hid_dbg(hid, "starting pid init\n");
|
||||||
|
|
||||||
if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
|
if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
|
||||||
|
|
Loading…
Reference in a new issue