lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20211213124122.25605-1-changlianzhi@uniontech.com>
Date:   Mon, 13 Dec 2021 20:41:22 +0800
From:   lianzhi chang <changlianzhi@...ontech.com>
To:     linux-kernel@...r.kernel.org
Cc:     dmitry.torokhov@...il.com, gregkh@...uxfoundation.org,
        jirislaby@...nel.org, andriy.shevchenko@...ux.intel.com,
        282827961@...com, lianzhi chang <changlianzhi@...ontech.com>
Subject: [PATCH v21] tty: Fix the keyboard led light display problem

Use the "ctrl+alt+Fn" key combination to switch the system from tty to
desktop or switch the system from desktop to tty. After the switch is
completed, it is found that the state of the keyboard lock is
inconsistent with the state of the keyboard Led light.The reasons are
as follows:

* The desktop environment (Xorg and other services) is bound to a tty
  (assuming it is tty1), and the kb->kbdmode attribute value of tty1
  will be set to VC_OFF. According to the current code logic, in the
  desktop environment, the values of ledstate and kb->ledflagstate
  of tty1 will not be modified anymore, so they are always 0.

* When switching between each tty, the final value of ledstate set by
  the previous tty is compared with the kb->ledflagstate value of the
  current tty to determine whether to set the state of the keyboard
  light. The process of switching between desktop and tty is also the
  process of switching between tty1 and other ttys. There are two
  situations:

  - (1) In the desktop environment, tty1 will not set the ledstate,
  which will cause when switching from the desktop to other ttys,
  if the desktop lights up the keyboard's led, after the switch is
  completed, the keyboard's led light will always be on;

  - (2) When switching from another tty to the desktop, this
  mechanism will trigger tty1 to set the led state. If other tty
  lights up the led of the keyboard before switching to the desktop,
  the led will be forcibly turned off. This situation should
  be avoided.

* Current patch explanation:When VT is switched,the keyboard LED
  status will be set to the current tty saved value;the value of
  kb->kbdledctl can be used to confirm whether the current VT
  can change the status of the keyboard led light;kb->kbdledctl
  is a new addition,you can use ioctl get or set.

Signed-off-by: lianzhi chang <changlianzhi@...ontech.com>
Suggested-by: Dmitry Torokhov <dmitry.torokhov@...il.com>
---
 v20:
 New solution: kbd_struct adds a new "kbdledctl" attribute,
 which can be obtained or changed through ioctl;
 "kbdledctl" is used to determine whether the current VT
 can set the keyboard led light;
 v21:
 Format correction
 
 drivers/tty/vt/keyboard.c | 61 +++++++++++++++++++++++++++++++++++++++
 drivers/tty/vt/vt_ioctl.c | 12 ++++++++
 include/linux/kbd_kern.h  |  4 +++
 include/linux/vt_kern.h   |  2 ++
 include/uapi/linux/kd.h   |  7 ++++-
 5 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/vt/keyboard.c b/drivers/tty/vt/keyboard.c
index c7fbbcdcc346..413e06a52648 100644
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -153,6 +153,7 @@ static int shift_state = 0;
 
 static unsigned int ledstate = -1U;			/* undefined */
 static unsigned char ledioctl;
+static bool vt_switch;
 
 /*
  * Notifier list for console keyboard events
@@ -414,6 +415,12 @@ void vt_set_leds_compute_shiftstate(void)
 {
 	unsigned long flags;
 
+	/*
+	 * When switching VT,according to the value of vt_switch,
+	 * judge whether it is necessary to force the keyboard light
+	 * state to be issued.
+	 */
+	vt_switch = true;
 	set_leds();
 
 	spin_lock_irqsave(&kbd_event_lock, flags);
@@ -1247,14 +1254,24 @@ void vt_kbd_con_stop(unsigned int console)
  */
 static void kbd_bh(struct tasklet_struct *unused)
 {
+	struct kbd_struct *kb;
 	unsigned int leds;
 	unsigned long flags;
 
+	kb = kbd_table + fg_console;
+	if (kb->kbdledctl == VC_LEDCTL_OFF)
+		return;
+
 	spin_lock_irqsave(&led_lock, flags);
 	leds = getleds();
 	leds |= (unsigned int)kbd->lockstate << 8;
 	spin_unlock_irqrestore(&led_lock, flags);
 
+	if (vt_switch) {
+		ledstate = ~leds;
+		vt_switch = false;
+	}
+
 	if (leds != ledstate) {
 		kbd_propagate_led_state(ledstate, leds);
 		ledstate = leds;
@@ -1857,6 +1874,35 @@ int vt_do_kdskbmode(unsigned int console, unsigned int arg)
 	return ret;
 }
 
+/**
+ *	vt_do_kdskbledctl		-	set whether VT can control led
+ *	@console: the console to use
+ *	@arg: the requested mode
+ *
+ *	Whether to allow the current vt to change the
+ *	keyboard light
+ */
+int vt_do_kdskbledctl(unsigned int console, unsigned int arg)
+{
+	struct kbd_struct *kb = &kbd_table[console];
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&kbd_event_lock, flags);
+	switch (arg) {
+	case K_LEDCTL_ON:
+		kb->kbdledctl = VC_LEDCTL_ON;
+		break;
+	case K_LEDCTL_OFF:
+		kb->kbdledctl = VC_LEDCTL_OFF;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&kbd_event_lock, flags);
+	return ret;
+}
+
 /**
  *	vt_do_kdskbmeta		-	set keyboard meta state
  *	@console: the console to use
@@ -2157,6 +2203,21 @@ int vt_do_kdgkbmode(unsigned int console)
 	}
 }
 
+int vt_do_kdgkbledctl(unsigned int console)
+{
+	struct kbd_struct *kb = &kbd_table[console];
+
+	/* This is a spot read so needs no locking */
+	switch (kb->kbdledctl) {
+	case VC_LEDCTL_ON:
+		return K_LEDCTL_ON;
+	case VC_LEDCTL_OFF:
+		return K_LEDCTL_OFF;
+	default:
+		return K_LEDCTL_ON;
+	}
+}
+
 /**
  *	vt_do_kdgkbmeta		-	report meta status
  *	@console: console to report
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 3639bb6dc372..c78bd452af55 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -405,6 +405,18 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
 	case KDGKBMODE:
 		return put_user(vt_do_kdgkbmode(console), (int __user *)arg);
 
+	case KDSKBLEDCTL:
+		if (!perm)
+			return -EPERM;
+		ret = vt_do_kdskbledctl(console, arg);
+		if (ret)
+			return ret;
+		tty_ldisc_flush(tty);
+		break;
+
+	case KDGKBLEDCTL:
+		return put_user(vt_do_kdgkbledctl(console), (int __user *)arg);
+
 	/* this could be folded into KDSKBMODE, but for compatibility
 	   reasons it is not so easy to fold KDGKBMETA into KDGKBMODE */
 	case KDSKBMETA:
diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
index c40811d79769..92c3d1ad3c28 100644
--- a/include/linux/kbd_kern.h
+++ b/include/linux/kbd_kern.h
@@ -32,6 +32,10 @@ struct kbd_struct {
 #define VC_CTRLRLOCK	KG_CTRLR 	/* ctrlr lock mode */
 	unsigned char slockstate; 	/* for `sticky' Shift, Ctrl, etc. */
 
+	unsigned char kbdledctl:1; /* Whether to allow to control the led of the keyboard */
+#define VC_LEDCTL_ON  0	/* VT can set the keyboard light */
+#define VC_LEDCTL_OFF 1	/* Prohibit VT to set the keyboard light */
+
 	unsigned char ledmode:1;
 #define LED_SHOW_FLAGS 0        /* traditional state */
 #define LED_SHOW_IOCTL 1        /* only change leds upon ioctl */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index b5ab452fca5b..67bdf3eddb5a 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -149,6 +149,7 @@ void hide_boot_cursor(bool hide);
 /* keyboard  provided interfaces */
 int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm);
 int vt_do_kdskbmode(unsigned int console, unsigned int arg);
+int vt_do_kdskbledctl(unsigned int console, unsigned int arg);
 int vt_do_kdskbmeta(unsigned int console, unsigned int arg);
 int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc,
 			  int perm);
@@ -157,6 +158,7 @@ int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm,
 int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm);
 int vt_do_kdskled(unsigned int console, int cmd, unsigned long arg, int perm);
 int vt_do_kdgkbmode(unsigned int console);
+int vt_do_kdgkbledctl(unsigned int console);
 int vt_do_kdgkbmeta(unsigned int console);
 void vt_reset_unicode(unsigned int console);
 int vt_get_shift_state(void);
diff --git a/include/uapi/linux/kd.h b/include/uapi/linux/kd.h
index ee929ece4112..138d636adf3e 100644
--- a/include/uapi/linux/kd.h
+++ b/include/uapi/linux/kd.h
@@ -86,6 +86,11 @@ struct unimapinit {
 #define KDGKBMODE	0x4B44	/* gets current keyboard mode */
 #define KDSKBMODE	0x4B45	/* sets current keyboard mode */
 
+#define K_LEDCTL_ON 0x00
+#define K_LEDCTL_OFF 0x01
+#define KDGKBLEDCTL  0x4B73  /* set whether to allow control of keyboard lights */
+#define KDSKBLEDCTL  0x4B74  /* get whether the keyboard light is currently allowed to be set */
+
 #define		K_METABIT	0x03
 #define		K_ESCPREFIX	0x04
 #define KDGKBMETA	0x4B62	/* gets meta key handling mode */
@@ -179,6 +184,6 @@ struct console_font {
 
 /* note: 0x4B00-0x4B4E all have had a value at some time;
    don't reuse for the time being */
-/* note: 0x4B60-0x4B6D, 0x4B70-0x4B72 used above */
+/* note: 0x4B60-0x4B6D, 0x4B70-0x4B74 used above */
 
 #endif /* _UAPI_LINUX_KD_H */
-- 
2.20.1



Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ