Skip to content

Commit 5137b35

Browse files
dtorJiri Kosina
authored andcommitted
HID: fix out of bound access in extract() and implement()
extract() and implement() access buffer containing reports in 64-bit chunks, but there is no guarantee that buffers are padded to 64 bit boundary. In fact, KASAN has caught such OOB access with i2c-hid and Synaptics touch controller. Instead of trying to hunt all parties that allocate buffers and make sure they are padded, let's switch extract() and implement() to byte access. It is a bit slower, bit we are not dealing with super fast devices here. Also let's fix link to the HID spec while we are at it. Signed-off-by: Dmitry Torokhov <[email protected]> Reviewed-by: Benjamin Tissoires <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent c2848f2 commit 5137b35

File tree

1 file changed

+66
-24
lines changed

1 file changed

+66
-24
lines changed

drivers/hid/hid-core.c

Lines changed: 66 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,28 +1075,45 @@ static u32 s32ton(__s32 value, unsigned n)
10751075
* Extract/implement a data field from/to a little endian report (bit array).
10761076
*
10771077
* Code sort-of follows HID spec:
1078-
* http://www.usb.org/developers/devclass_docs/HID1_11.pdf
1078+
* http://www.usb.org/developers/hidpage/HID1_11.pdf
10791079
*
10801080
* While the USB HID spec allows unlimited length bit fields in "report
10811081
* descriptors", most devices never use more than 16 bits.
10821082
* One model of UPS is claimed to report "LINEV" as a 32-bit field.
10831083
* Search linux-kernel and linux-usb-devel archives for "hid-core extract".
10841084
*/
10851085

1086-
__u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
1087-
unsigned offset, unsigned n)
1088-
{
1089-
u64 x;
1086+
static u32 __extract(u8 *report, unsigned offset, int n)
1087+
{
1088+
unsigned int idx = offset / 8;
1089+
unsigned int bit_nr = 0;
1090+
unsigned int bit_shift = offset % 8;
1091+
int bits_to_copy = 8 - bit_shift;
1092+
u32 value = 0;
1093+
u32 mask = n < 32 ? (1U << n) - 1 : ~0U;
1094+
1095+
while (n > 0) {
1096+
value |= ((u32)report[idx] >> bit_shift) << bit_nr;
1097+
n -= bits_to_copy;
1098+
bit_nr += bits_to_copy;
1099+
bits_to_copy = 8;
1100+
bit_shift = 0;
1101+
idx++;
1102+
}
1103+
1104+
return value & mask;
1105+
}
10901106

1091-
if (n > 32)
1107+
u32 hid_field_extract(const struct hid_device *hid, u8 *report,
1108+
unsigned offset, unsigned n)
1109+
{
1110+
if (n > 32) {
10921111
hid_warn(hid, "hid_field_extract() called with n (%d) > 32! (%s)\n",
10931112
n, current->comm);
1113+
n = 32;
1114+
}
10941115

1095-
report += offset >> 3; /* adjust byte index */
1096-
offset &= 7; /* now only need bit offset into one byte */
1097-
x = get_unaligned_le64(report);
1098-
x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */
1099-
return (u32) x;
1116+
return __extract(report, offset, n);
11001117
}
11011118
EXPORT_SYMBOL_GPL(hid_field_extract);
11021119

@@ -1106,31 +1123,56 @@ EXPORT_SYMBOL_GPL(hid_field_extract);
11061123
* The data mangled in the bit stream remains in little endian
11071124
* order the whole time. It make more sense to talk about
11081125
* endianness of register values by considering a register
1109-
* a "cached" copy of the little endiad bit stream.
1126+
* a "cached" copy of the little endian bit stream.
11101127
*/
1111-
static void implement(const struct hid_device *hid, __u8 *report,
1112-
unsigned offset, unsigned n, __u32 value)
1128+
1129+
static void __implement(u8 *report, unsigned offset, int n, u32 value)
1130+
{
1131+
unsigned int idx = offset / 8;
1132+
unsigned int size = offset + n;
1133+
unsigned int bit_shift = offset % 8;
1134+
int bits_to_set = 8 - bit_shift;
1135+
u8 bit_mask = 0xff << bit_shift;
1136+
1137+
while (n - bits_to_set >= 0) {
1138+
report[idx] &= ~bit_mask;
1139+
report[idx] |= value << bit_shift;
1140+
value >>= bits_to_set;
1141+
n -= bits_to_set;
1142+
bits_to_set = 8;
1143+
bit_mask = 0xff;
1144+
bit_shift = 0;
1145+
idx++;
1146+
}
1147+
1148+
/* last nibble */
1149+
if (n) {
1150+
if (size % 8)
1151+
bit_mask &= (1U << (size % 8)) - 1;
1152+
report[idx] &= ~bit_mask;
1153+
report[idx] |= (value << bit_shift) & bit_mask;
1154+
}
1155+
}
1156+
1157+
static void implement(const struct hid_device *hid, u8 *report,
1158+
unsigned offset, unsigned n, u32 value)
11131159
{
1114-
u64 x;
1115-
u64 m = (1ULL << n) - 1;
1160+
u64 m;
11161161

1117-
if (n > 32)
1162+
if (n > 32) {
11181163
hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n",
11191164
__func__, n, current->comm);
1165+
n = 32;
1166+
}
11201167

1168+
m = (1ULL << n) - 1;
11211169
if (value > m)
11221170
hid_warn(hid, "%s() called with too large value %d! (%s)\n",
11231171
__func__, value, current->comm);
11241172
WARN_ON(value > m);
11251173
value &= m;
11261174

1127-
report += offset >> 3;
1128-
offset &= 7;
1129-
1130-
x = get_unaligned_le64(report);
1131-
x &= ~(m << offset);
1132-
x |= ((u64)value) << offset;
1133-
put_unaligned_le64(x, report);
1175+
__implement(report, offset, n, value);
11341176
}
11351177

11361178
/*

0 commit comments

Comments
 (0)