|
|
@@ -123,6 +123,7 @@ static const int NR_TYPES = ARRAY_SIZE(max_vals);
|
|
|
static struct input_handler kbd_handler;
|
|
|
static DEFINE_SPINLOCK(kbd_event_lock);
|
|
|
static DEFINE_SPINLOCK(led_lock);
|
|
|
+static DEFINE_SPINLOCK(func_buf_lock); /* guard 'func_buf' and friends */
|
|
|
static unsigned long key_down[BITS_TO_LONGS(KEY_CNT)]; /* keyboard key bitmap */
|
|
|
static unsigned char shift_down[NR_SHIFT]; /* shift state counters.. */
|
|
|
static bool dead_key_next;
|
|
|
@@ -1990,11 +1991,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
|
|
char *p;
|
|
|
u_char *q;
|
|
|
u_char __user *up;
|
|
|
- int sz;
|
|
|
+ int sz, fnw_sz;
|
|
|
int delta;
|
|
|
char *first_free, *fj, *fnw;
|
|
|
int i, j, k;
|
|
|
int ret;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
if (!capable(CAP_SYS_TTY_CONFIG))
|
|
|
perm = 0;
|
|
|
@@ -2037,7 +2039,14 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
|
|
goto reterr;
|
|
|
}
|
|
|
|
|
|
+ fnw = NULL;
|
|
|
+ fnw_sz = 0;
|
|
|
+ /* race aginst other writers */
|
|
|
+ again:
|
|
|
+ spin_lock_irqsave(&func_buf_lock, flags);
|
|
|
q = func_table[i];
|
|
|
+
|
|
|
+ /* fj pointer to next entry after 'q' */
|
|
|
first_free = funcbufptr + (funcbufsize - funcbufleft);
|
|
|
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++)
|
|
|
;
|
|
|
@@ -2045,10 +2054,12 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
|
|
fj = func_table[j];
|
|
|
else
|
|
|
fj = first_free;
|
|
|
-
|
|
|
+ /* buffer usage increase by new entry */
|
|
|
delta = (q ? -strlen(q) : 1) + strlen(kbs->kb_string);
|
|
|
+
|
|
|
if (delta <= funcbufleft) { /* it fits in current buf */
|
|
|
if (j < MAX_NR_FUNC) {
|
|
|
+ /* make enough space for new entry at 'fj' */
|
|
|
memmove(fj + delta, fj, first_free - fj);
|
|
|
for (k = j; k < MAX_NR_FUNC; k++)
|
|
|
if (func_table[k])
|
|
|
@@ -2061,20 +2072,28 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
|
|
sz = 256;
|
|
|
while (sz < funcbufsize - funcbufleft + delta)
|
|
|
sz <<= 1;
|
|
|
- fnw = kmalloc(sz, GFP_KERNEL);
|
|
|
- if(!fnw) {
|
|
|
- ret = -ENOMEM;
|
|
|
- goto reterr;
|
|
|
+ if (fnw_sz != sz) {
|
|
|
+ spin_unlock_irqrestore(&func_buf_lock, flags);
|
|
|
+ kfree(fnw);
|
|
|
+ fnw = kmalloc(sz, GFP_KERNEL);
|
|
|
+ fnw_sz = sz;
|
|
|
+ if (!fnw) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto reterr;
|
|
|
+ }
|
|
|
+ goto again;
|
|
|
}
|
|
|
|
|
|
if (!q)
|
|
|
func_table[i] = fj;
|
|
|
+ /* copy data before insertion point to new location */
|
|
|
if (fj > funcbufptr)
|
|
|
memmove(fnw, funcbufptr, fj - funcbufptr);
|
|
|
for (k = 0; k < j; k++)
|
|
|
if (func_table[k])
|
|
|
func_table[k] = fnw + (func_table[k] - funcbufptr);
|
|
|
|
|
|
+ /* copy data after insertion point to new location */
|
|
|
if (first_free > fj) {
|
|
|
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
|
|
|
for (k = j; k < MAX_NR_FUNC; k++)
|
|
|
@@ -2087,7 +2106,9 @@ int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm)
|
|
|
funcbufleft = funcbufleft - delta + sz - funcbufsize;
|
|
|
funcbufsize = sz;
|
|
|
}
|
|
|
+ /* finally insert item itself */
|
|
|
strcpy(func_table[i], kbs->kb_string);
|
|
|
+ spin_unlock_irqrestore(&func_buf_lock, flags);
|
|
|
break;
|
|
|
}
|
|
|
ret = 0;
|