Blob Blame History Raw
From e64242caef18b4a5840b0e7a9bff37abd4f4f933 Mon Sep 17 00:00:00 2001
From: Helge Deller <deller@gmx.de>
Date: Sat, 25 Jun 2022 13:00:34 +0200
Subject: [PATCH] fbcon: Prevent that screen size is smaller than font size
Git-commit: e64242caef18b4a5840b0e7a9bff37abd4f4f933
Patch-mainline: v5.19-rc6
References: CVE-2021-33655 bsc#1201635

[ backport note: the patch was heavily modified to be adaptable to the
  old kernel code where both fbcon and fbmem were still separated.
  The original patch exports fbcon_modechange_possible() from fbcon to
  be used by fbmem, but it's not possible with the old kernel.
  Hence, this patch makes fbcon_modechange_possible() to be called via
  the fb notifier instead.  fbmem calls the notifier with the new event
  type. -- tiwai ]

We need to prevent that users configure a screen size which is smaller than the
currently selected font size. Otherwise rendering chars on the screen will
access memory outside the graphics memory region.

This patch adds a new function fbcon_modechange_possible() which
implements this check and which later may be extended with other checks
if necessary.  The new function is called from the FBIOPUT_VSCREENINFO
ioctl handler in fbmem.c, which will return -EINVAL if userspace asked
for a too small screen size.

Signed-off-by: Helge Deller <deller@gmx.de>
Reviewed-by: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: stable@vger.kernel.org # v5.4+
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/video/console/fbcon.c    |   29 +++++++++++++++++++++++++++++
 drivers/video/fbdev/core/fbmem.c |    7 ++++++-
 include/linux/fb.h               |    2 ++
 3 files changed, 37 insertions(+), 1 deletion(-)

--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -2676,6 +2676,32 @@ static void fbcon_set_all_vcs(struct fb_
 		fbcon_modechanged(info);
 }
 
+/* let fbcon check if it supports a new screen resolution */
+static int fbcon_modechange_possible(struct fb_info *info,
+				     struct fb_var_screeninfo *var)
+{
+	struct fbcon_ops *ops = info->fbcon_par;
+	struct vc_data *vc;
+	unsigned int i;
+
+	if (!ops)
+		return 0;
+
+	/* prevent setting a screen size which is smaller than font size */
+	for (i = first_fb_vc; i <= last_fb_vc; i++) {
+		vc = vc_cons[i].d;
+		if (!vc || vc->vc_mode != KD_TEXT ||
+			   registered_fb[con2fb_map[i]] != info)
+			continue;
+
+		if (vc->vc_font.width  > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
+		    vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
+			return notifier_from_errno(-EINVAL);
+	}
+
+	return 0;
+}
+
 static int fbcon_mode_deleted(struct fb_info *info,
 			      struct fb_videomode *mode)
 {
@@ -3031,6 +3057,9 @@ static int fbcon_event_notify(struct not
 		idx = info->node;
 		fbcon_remap_all(idx);
 		break;
+	case FB_EVENT_MODE_CHANGE_CHECK:
+		ret = fbcon_modechange_possible(event->info, event->data);
+		break;
 	}
 done:
 	return ret;
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1123,7 +1123,12 @@ static long do_fb_ioctl(struct fb_info *
 			return -ENODEV;
 		}
 		info->flags |= FBINFO_MISC_USEREVENT;
-		ret = fb_set_var(info, &var);
+		event.info = info;
+		event.data = &var;
+		ret = fb_notifier_call_chain(FB_EVENT_MODE_CHANGE_CHECK, &event);
+		ret = notifier_to_errno(ret);
+		if (!ret)
+			ret = fb_set_var(info, &var);
 		info->flags &= ~FBINFO_MISC_USEREVENT;
 		unlock_fb_info(info);
 		console_unlock();
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -162,6 +162,8 @@ struct fb_cursor_user {
 #define FB_EARLY_EVENT_BLANK		0x10
 /*      A hardware display blank revert early change occured */
 #define FB_R_EARLY_EVENT_BLANK		0x11
+/* pre-check for mode change (used only by fbcon) */
+#define FB_EVENT_MODE_CHANGE_CHECK	0x12
 
 struct fb_event {
 	struct fb_info *info;