mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
tty: vt/keyboard: Split apart vt_do_diacrit()
After commitbfb24564b5("tty: vt/keyboard: use __free()"), builds using asm goto for put_user() and get_user() with a version of clang older than 17 error with: drivers/tty/vt/keyboard.c:1709:7: error: cannot jump from this asm goto statement to one of its possible targets if (put_user(asize, &a->kb_cnt)) ^ ... arch/arm64/include/asm/uaccess.h:298:2: note: expanded from macro '__put_mem_asm' asm goto( \ ^ drivers/tty/vt/keyboard.c:1687:7: note: possible target of asm goto statement if (put_user(asize, &a->kb_cnt)) ^ ... arch/arm64/include/asm/uaccess.h:342:2: note: expanded from macro '__raw_put_user' __rpu_failed: \ ^ drivers/tty/vt/keyboard.c:1697:23: note: jump exits scope of variable with __attribute__((cleanup)) void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc), ^ drivers/tty/vt/keyboard.c:1671:33: note: jump bypasses initialization of variable with __attribute__((cleanup)) struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr), ^ Prior to a fix to clang's scope checker in clang 17 [1], all labels in a function were validated as potential targets of all asm gotos in a function, regardless of whether they actually were a target of an asm goto call, resulting in false positive errors about skipping over variables marked with the cleanup attribute. To workaround this error, split up the bodies of the case statements in vt_do_diacrit() into their own functions so that the scope checker does not trip up on the multiple instances of __free(). Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202509091702.Oc7eCRDw-lkp@intel.com/ Closes: https://lore.kernel.org/oe-kbuild-all/202511241835.EA8lShgH-lkp@intel.com/ Link:f023f5cdb2[1] Signed-off-by: Nathan Chancellor <nathan@kernel.org> Link: https://patch.msgid.link/20251125-tty-vt-keyboard-wa-clang-scope-check-error-v1-1-f5a5ea55c578@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
485c13d9bc
commit
0a76a17238
1 changed files with 121 additions and 112 deletions
|
|
@ -1649,6 +1649,123 @@ int __init kbd_init(void)
|
|||
|
||||
/* Ioctl support code */
|
||||
|
||||
static int vt_do_kdgkbdiacr(void __user *udp)
|
||||
{
|
||||
struct kbdiacrs __user *a = udp;
|
||||
int i, asize;
|
||||
|
||||
struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
|
||||
GFP_KERNEL);
|
||||
if (!dia)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Lock the diacriticals table, make a copy and then
|
||||
copy it after we unlock */
|
||||
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||
asize = accent_table_size;
|
||||
for (i = 0; i < asize; i++) {
|
||||
dia[i].diacr = conv_uni_to_8bit(accent_table[i].diacr);
|
||||
dia[i].base = conv_uni_to_8bit(accent_table[i].base);
|
||||
dia[i].result = conv_uni_to_8bit(accent_table[i].result);
|
||||
}
|
||||
}
|
||||
|
||||
if (put_user(asize, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vt_do_kdgkbdiacruc(void __user *udp)
|
||||
{
|
||||
struct kbdiacrsuc __user *a = udp;
|
||||
int asize;
|
||||
|
||||
void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
|
||||
GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Lock the diacriticals table, make a copy and then
|
||||
copy it after we unlock */
|
||||
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||
asize = accent_table_size;
|
||||
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
|
||||
}
|
||||
|
||||
if (put_user(asize, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(a->kbdiacruc, buf, asize * sizeof(struct kbdiacruc)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vt_do_kdskbdiacr(void __user *udp, int perm)
|
||||
{
|
||||
struct kbdiacrs __user *a = udp;
|
||||
struct kbdiacr __free(kfree) *dia = NULL;
|
||||
unsigned int ct;
|
||||
int i;
|
||||
|
||||
if (!perm)
|
||||
return -EPERM;
|
||||
if (get_user(ct, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (ct >= MAX_DIACR)
|
||||
return -EINVAL;
|
||||
|
||||
if (ct) {
|
||||
dia = memdup_array_user(a->kbdiacr,
|
||||
ct, sizeof(struct kbdiacr));
|
||||
if (IS_ERR(dia))
|
||||
return PTR_ERR(dia);
|
||||
}
|
||||
|
||||
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||
accent_table_size = ct;
|
||||
for (i = 0; i < ct; i++) {
|
||||
accent_table[i].diacr =
|
||||
conv_8bit_to_uni(dia[i].diacr);
|
||||
accent_table[i].base =
|
||||
conv_8bit_to_uni(dia[i].base);
|
||||
accent_table[i].result =
|
||||
conv_8bit_to_uni(dia[i].result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vt_do_kdskbdiacruc(void __user *udp, int perm)
|
||||
{
|
||||
struct kbdiacrsuc __user *a = udp;
|
||||
unsigned int ct;
|
||||
void __free(kfree) *buf = NULL;
|
||||
|
||||
if (!perm)
|
||||
return -EPERM;
|
||||
|
||||
if (get_user(ct, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
|
||||
if (ct >= MAX_DIACR)
|
||||
return -EINVAL;
|
||||
|
||||
if (ct) {
|
||||
buf = memdup_array_user(a->kbdiacruc,
|
||||
ct, sizeof(struct kbdiacruc));
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
}
|
||||
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||
if (ct)
|
||||
memcpy(accent_table, buf,
|
||||
ct * sizeof(struct kbdiacruc));
|
||||
accent_table_size = ct;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* vt_do_diacrit - diacritical table updates
|
||||
* @cmd: ioctl request
|
||||
|
|
@ -1660,123 +1777,15 @@ int __init kbd_init(void)
|
|||
*/
|
||||
int vt_do_diacrit(unsigned int cmd, void __user *udp, int perm)
|
||||
{
|
||||
int asize;
|
||||
|
||||
switch (cmd) {
|
||||
case KDGKBDIACR:
|
||||
{
|
||||
struct kbdiacrs __user *a = udp;
|
||||
int i;
|
||||
|
||||
struct kbdiacr __free(kfree) *dia = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacr),
|
||||
GFP_KERNEL);
|
||||
if (!dia)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Lock the diacriticals table, make a copy and then
|
||||
copy it after we unlock */
|
||||
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||
asize = accent_table_size;
|
||||
for (i = 0; i < asize; i++) {
|
||||
dia[i].diacr = conv_uni_to_8bit(accent_table[i].diacr);
|
||||
dia[i].base = conv_uni_to_8bit(accent_table[i].base);
|
||||
dia[i].result = conv_uni_to_8bit(accent_table[i].result);
|
||||
}
|
||||
}
|
||||
|
||||
if (put_user(asize, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(a->kbdiacr, dia, asize * sizeof(struct kbdiacr)))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
return vt_do_kdgkbdiacr(udp);
|
||||
case KDGKBDIACRUC:
|
||||
{
|
||||
struct kbdiacrsuc __user *a = udp;
|
||||
|
||||
void __free(kfree) *buf = kmalloc_array(MAX_DIACR, sizeof(struct kbdiacruc),
|
||||
GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Lock the diacriticals table, make a copy and then
|
||||
copy it after we unlock */
|
||||
scoped_guard(spinlock_irqsave, &kbd_event_lock) {
|
||||
asize = accent_table_size;
|
||||
memcpy(buf, accent_table, asize * sizeof(struct kbdiacruc));
|
||||
}
|
||||
|
||||
if (put_user(asize, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (copy_to_user(a->kbdiacruc, buf, asize * sizeof(struct kbdiacruc)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vt_do_kdgkbdiacruc(udp);
|
||||
case KDSKBDIACR:
|
||||
{
|
||||
struct kbdiacrs __user *a = udp;
|
||||
struct kbdiacr __free(kfree) *dia = NULL;
|
||||
unsigned int ct;
|
||||
int i;
|
||||
|
||||
if (!perm)
|
||||
return -EPERM;
|
||||
if (get_user(ct, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
if (ct >= MAX_DIACR)
|
||||
return -EINVAL;
|
||||
|
||||
if (ct) {
|
||||
dia = memdup_array_user(a->kbdiacr,
|
||||
ct, sizeof(struct kbdiacr));
|
||||
if (IS_ERR(dia))
|
||||
return PTR_ERR(dia);
|
||||
}
|
||||
|
||||
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||
accent_table_size = ct;
|
||||
for (i = 0; i < ct; i++) {
|
||||
accent_table[i].diacr =
|
||||
conv_8bit_to_uni(dia[i].diacr);
|
||||
accent_table[i].base =
|
||||
conv_8bit_to_uni(dia[i].base);
|
||||
accent_table[i].result =
|
||||
conv_8bit_to_uni(dia[i].result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return vt_do_kdskbdiacr(udp, perm);
|
||||
case KDSKBDIACRUC:
|
||||
{
|
||||
struct kbdiacrsuc __user *a = udp;
|
||||
unsigned int ct;
|
||||
void __free(kfree) *buf = NULL;
|
||||
|
||||
if (!perm)
|
||||
return -EPERM;
|
||||
|
||||
if (get_user(ct, &a->kb_cnt))
|
||||
return -EFAULT;
|
||||
|
||||
if (ct >= MAX_DIACR)
|
||||
return -EINVAL;
|
||||
|
||||
if (ct) {
|
||||
buf = memdup_array_user(a->kbdiacruc,
|
||||
ct, sizeof(struct kbdiacruc));
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
}
|
||||
guard(spinlock_irqsave)(&kbd_event_lock);
|
||||
if (ct)
|
||||
memcpy(accent_table, buf,
|
||||
ct * sizeof(struct kbdiacruc));
|
||||
accent_table_size = ct;
|
||||
return 0;
|
||||
}
|
||||
return vt_do_kdskbdiacruc(udp, perm);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue