From 0a76a17238f805b231d97b118232a5185bbb7a18 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 25 Nov 2025 15:54:37 -0700 Subject: [PATCH] tty: vt/keyboard: Split apart vt_do_diacrit() After commit bfb24564b5fd ("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 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: https://github.com/llvm/llvm-project/commit/f023f5cdb2e6c19026f04a15b5a935c041835d14 [1] Signed-off-by: Nathan Chancellor 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 --- drivers/tty/vt/keyboard.c | 233 ++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 112 deletions(-) diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c index d65fc60dd7be..3538d54d6a6a 100644 --- a/drivers/tty/vt/keyboard.c +++ b/drivers/tty/vt/keyboard.c @@ -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; }