Skip to content

Commit 0774d19

Browse files
committed
Input: try trimming too long modalias strings
If an input device declares too many capability bits then modalias string for such device may become too long and not fit into uevent buffer, resulting in failure of sending said uevent. This, in turn, may prevent userspace from recognizing existence of such devices. This is typically not a concern for real hardware devices as they have limited number of keys, but happen with synthetic devices such as ones created by xen-kbdfront driver, which creates devices as being capable of delivering all possible keys, since it doesn't know what keys the backend may produce. To deal with such devices input core will attempt to trim key data, in the hope that the rest of modalias string will fit in the given buffer. When trimming key data it will indicate that it is not complete by placing "+," sign, resulting in conversions like this: old: k71,72,73,74,78,7A,7B,7C,7D,8E,9E,A4,AD,E0,E1,E4,F8,174, new: k71,72,73,74,78,7A,7B,7C,+, This should allow existing udev rules continue to work with existing devices, and will also allow writing more complex rules that would recognize trimmed modalias and check input device characteristics by other means (for example by parsing KEY= data in uevent or parsing input device sysfs attributes). Note that the driver core may try adding more uevent environment variables once input core is done adding its own, so when forming modalias we can not use the entire available buffer, so we reduce it by somewhat an arbitrary amount (96 bytes). Reported-by: Jason Andryuk <[email protected]> Reviewed-by: Peter Hutterer <[email protected]> Tested-by: Jason Andryuk <[email protected]> Link: https://lore.kernel.org/r/[email protected] Cc: [email protected] Signed-off-by: Dmitry Torokhov <[email protected]>
1 parent 5852f2a commit 0774d19

File tree

1 file changed

+89
-15
lines changed

1 file changed

+89
-15
lines changed

drivers/input/input.c

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,19 +1378,19 @@ static int input_print_modalias_bits(char *buf, int size,
13781378
char name, const unsigned long *bm,
13791379
unsigned int min_bit, unsigned int max_bit)
13801380
{
1381-
int len = 0, i;
1381+
int bit = min_bit;
1382+
int len = 0;
13821383

13831384
len += snprintf(buf, max(size, 0), "%c", name);
1384-
for (i = min_bit; i < max_bit; i++)
1385-
if (bm[BIT_WORD(i)] & BIT_MASK(i))
1386-
len += snprintf(buf + len, max(size - len, 0), "%X,", i);
1385+
for_each_set_bit_from(bit, bm, max_bit)
1386+
len += snprintf(buf + len, max(size - len, 0), "%X,", bit);
13871387
return len;
13881388
}
13891389

1390-
static int input_print_modalias(char *buf, int size, const struct input_dev *id,
1391-
int add_cr)
1390+
static int input_print_modalias_parts(char *buf, int size, int full_len,
1391+
const struct input_dev *id)
13921392
{
1393-
int len;
1393+
int len, klen, remainder, space;
13941394

13951395
len = snprintf(buf, max(size, 0),
13961396
"input:b%04Xv%04Xp%04Xe%04X-",
@@ -1399,8 +1399,48 @@ static int input_print_modalias(char *buf, int size, const struct input_dev *id,
13991399

14001400
len += input_print_modalias_bits(buf + len, size - len,
14011401
'e', id->evbit, 0, EV_MAX);
1402-
len += input_print_modalias_bits(buf + len, size - len,
1402+
1403+
/*
1404+
* Calculate the remaining space in the buffer making sure we
1405+
* have place for the terminating 0.
1406+
*/
1407+
space = max(size - (len + 1), 0);
1408+
1409+
klen = input_print_modalias_bits(buf + len, size - len,
14031410
'k', id->keybit, KEY_MIN_INTERESTING, KEY_MAX);
1411+
len += klen;
1412+
1413+
/*
1414+
* If we have more data than we can fit in the buffer, check
1415+
* if we can trim key data to fit in the rest. We will indicate
1416+
* that key data is incomplete by adding "+" sign at the end, like
1417+
* this: * "k1,2,3,45,+,".
1418+
*
1419+
* Note that we shortest key info (if present) is "k+," so we
1420+
* can only try to trim if key data is longer than that.
1421+
*/
1422+
if (full_len && size < full_len + 1 && klen > 3) {
1423+
remainder = full_len - len;
1424+
/*
1425+
* We can only trim if we have space for the remainder
1426+
* and also for at least "k+," which is 3 more characters.
1427+
*/
1428+
if (remainder <= space - 3) {
1429+
/*
1430+
* We are guaranteed to have 'k' in the buffer, so
1431+
* we need at least 3 additional bytes for storing
1432+
* "+," in addition to the remainder.
1433+
*/
1434+
for (int i = size - 1 - remainder - 3; i >= 0; i--) {
1435+
if (buf[i] == 'k' || buf[i] == ',') {
1436+
strcpy(buf + i + 1, "+,");
1437+
len = i + 3; /* Not counting '\0' */
1438+
break;
1439+
}
1440+
}
1441+
}
1442+
}
1443+
14041444
len += input_print_modalias_bits(buf + len, size - len,
14051445
'r', id->relbit, 0, REL_MAX);
14061446
len += input_print_modalias_bits(buf + len, size - len,
@@ -1416,20 +1456,35 @@ static int input_print_modalias(char *buf, int size, const struct input_dev *id,
14161456
len += input_print_modalias_bits(buf + len, size - len,
14171457
'w', id->swbit, 0, SW_MAX);
14181458

1419-
if (add_cr)
1420-
len += snprintf(buf + len, max(size - len, 0), "\n");
1421-
14221459
return len;
14231460
}
14241461

1462+
static int input_print_modalias(char *buf, int size, const struct input_dev *id)
1463+
{
1464+
int full_len;
1465+
1466+
/*
1467+
* Printing is done in 2 passes: first one figures out total length
1468+
* needed for the modalias string, second one will try to trim key
1469+
* data in case when buffer is too small for the entire modalias.
1470+
* If the buffer is too small regardless, it will fill as much as it
1471+
* can (without trimming key data) into the buffer and leave it to
1472+
* the caller to figure out what to do with the result.
1473+
*/
1474+
full_len = input_print_modalias_parts(NULL, 0, 0, id);
1475+
return input_print_modalias_parts(buf, size, full_len, id);
1476+
}
1477+
14251478
static ssize_t input_dev_show_modalias(struct device *dev,
14261479
struct device_attribute *attr,
14271480
char *buf)
14281481
{
14291482
struct input_dev *id = to_input_dev(dev);
14301483
ssize_t len;
14311484

1432-
len = input_print_modalias(buf, PAGE_SIZE, id, 1);
1485+
len = input_print_modalias(buf, PAGE_SIZE, id);
1486+
if (len < PAGE_SIZE - 2)
1487+
len += snprintf(buf + len, PAGE_SIZE - len, "\n");
14331488

14341489
return min_t(int, len, PAGE_SIZE);
14351490
}
@@ -1641,6 +1696,23 @@ static int input_add_uevent_bm_var(struct kobj_uevent_env *env,
16411696
return 0;
16421697
}
16431698

1699+
/*
1700+
* This is a pretty gross hack. When building uevent data the driver core
1701+
* may try adding more environment variables to kobj_uevent_env without
1702+
* telling us, so we have no idea how much of the buffer we can use to
1703+
* avoid overflows/-ENOMEM elsewhere. To work around this let's artificially
1704+
* reduce amount of memory we will use for the modalias environment variable.
1705+
*
1706+
* The potential additions are:
1707+
*
1708+
* SEQNUM=18446744073709551615 - (%llu - 28 bytes)
1709+
* HOME=/ (6 bytes)
1710+
* PATH=/sbin:/bin:/usr/sbin:/usr/bin (34 bytes)
1711+
*
1712+
* 68 bytes total. Allow extra buffer - 96 bytes
1713+
*/
1714+
#define UEVENT_ENV_EXTRA_LEN 96
1715+
16441716
static int input_add_uevent_modalias_var(struct kobj_uevent_env *env,
16451717
const struct input_dev *dev)
16461718
{
@@ -1650,9 +1722,11 @@ static int input_add_uevent_modalias_var(struct kobj_uevent_env *env,
16501722
return -ENOMEM;
16511723

16521724
len = input_print_modalias(&env->buf[env->buflen - 1],
1653-
sizeof(env->buf) - env->buflen,
1654-
dev, 0);
1655-
if (len >= (sizeof(env->buf) - env->buflen))
1725+
(int)sizeof(env->buf) - env->buflen -
1726+
UEVENT_ENV_EXTRA_LEN,
1727+
dev);
1728+
if (len >= ((int)sizeof(env->buf) - env->buflen -
1729+
UEVENT_ENV_EXTRA_LEN))
16561730
return -ENOMEM;
16571731

16581732
env->buflen += len;

0 commit comments

Comments
 (0)