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> CC: <stable@vger.kernel.org> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Lee Jones <lee.jones@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: Ica4d1b6adc1bcb30ce077f7d954cb8ba94bce730
This commit is contained in:
parent
72fb1a1e6c
commit
187d27cc7e
|
@ -75,13 +75,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;
|
||||||
|
|
|
@ -87,13 +87,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;
|
||||||
|
|
|
@ -59,13 +59,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;
|
||||||
|
|
|
@ -77,14 +77,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;
|
||||||
|
|
|
@ -140,13 +140,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;
|
||||||
|
|
|
@ -62,11 +62,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)
|
||||||
|
|
|
@ -129,12 +129,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;
|
||||||
|
|
|
@ -517,14 +517,21 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
|
||||||
|
|
||||||
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 lg_drv_data *drv_data;
|
struct lg_drv_data *drv_data;
|
||||||
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;
|
||||||
|
|
|
@ -127,12 +127,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;
|
||||||
|
|
|
@ -126,12 +126,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;
|
||||||
|
|
|
@ -66,11 +66,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)
|
||||||
|
|
Loading…
Reference in New Issue