Vulnerability: A race problem was seen in the vt_k_ioctl in drivers/tty/vt/vt_ioctl.c in the Linux kernel, which may cause an out of bounds read in vt as the write access to vc_mode is not protected by lock-in vt_ioctl (KDSETMDE). The highest threat from this vulnerability is to data confidentiality.
Vulnerability ID: CVE-2021-3753
CWE: CWE-362
Severity score: 2.9
Project: https://github.com/torvalds/linux
Published: 2022-02-16
File: drivers/tty/vt/vt_ioctl.c
Commit message: vt_kdsetmode: extend console locking

As per the long-suffering comment.

Reported-by: Minh Yuan <yuanmingbuaa@gmail.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Commit author: Linus Torvalds

Vulnerable version (before fix):
 int vt_waitactive(int n)  *  * XXX It should at least call into the driver, fbdev's definitely need to  * restore their engine state. --BenH  */ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) { static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) 		return -EINVAL; 	}  	/* FIXME: this needs the console lock extending */ 	if (vc->vc_mode == mode) 		return 0;  static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) 		return 0;  	/* explicitly blank/unblank the screen if switching modes */ 	console_lock(); 	if (mode == KD_TEXT) 		do_unblank_screen(1); 	else 		do_blank_screen(1); 	console_unlock();  	return 0; } static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, 		if (!perm) 			return -EPERM;  		return vt_kdsetmode(vc, arg);  	case KDGETMODE: 		return put_user(vc->vc_mode, (int __user *)arg);

Fixed version (after fix):
 int vt_waitactive(int n)  *  * XXX It should at least call into the driver, fbdev's definitely need to  * restore their engine state. --BenH  *  * Called with the console lock held.  */ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) { static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) 		return -EINVAL; 	}  	if (vc->vc_mode == mode) 		return 0;  static int vt_kdsetmode(struct vc_data *vc, unsigned long mode) 		return 0;  	/* explicitly blank/unblank the screen if switching modes */ 	if (mode == KD_TEXT) 		do_unblank_screen(1); 	else 		do_blank_screen(1);  	return 0; } static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd, 		if (!perm) 			return -EPERM;  		console_lock(); 		ret = vt_kdsetmode(vc, arg); 		console_unlock(); 		return ret;  	case KDGETMODE: 		return put_user(vc->vc_mode, (int __user *)arg);

Patch:
patch: @@ -246,6 +246,8 @@ int vt_waitactive(int n)
  *
  * XXX It should at least call into the driver, fbdev's definitely need to
  * restore their engine state. --BenH
+ *
+ * Called with the console lock held.
  */
 static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
 {
@@ -262,7 +264,6 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
 		return -EINVAL;
 	}
 
-	/* FIXME: this needs the console lock extending */
 	if (vc->vc_mode == mode)
 		return 0;
 
@@ -271,12 +272,10 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)
 		return 0;
 
 	/* explicitly blank/unblank the screen if switching modes */
-	console_lock();
 	if (mode == KD_TEXT)
 		do_unblank_screen(1);
 	else
 		do_blank_screen(1);
-	console_unlock();
 
 	return 0;
 }
@@ -378,7 +377,10 @@ static int vt_k_ioctl(struct tty_struct *tty, unsigned int cmd,
 		if (!perm)
 			return -EPERM;
 
-		return vt_kdsetmode(vc, arg);
+		console_lock();
+		ret = vt_kdsetmode(vc, arg);
+		console_unlock();
+		return ret;
 
 	case KDGETMODE:
 		return put_user(vc->vc_mode, (int __user *)arg);
path: drivers/tty/vt/vt_ioctl.c