tty: vt/keyboard: Split apart vt_do_diacrit()

After commit bfb24564b5 ("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:
Nathan Chancellor 2025-11-25 15:54:37 -07:00 committed by Greg Kroah-Hartman
parent 485c13d9bc
commit 0a76a17238

View file

@ -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;
}