Takashi Iwai 1f7d54
From e64242caef18b4a5840b0e7a9bff37abd4f4f933 Mon Sep 17 00:00:00 2001
Takashi Iwai 1f7d54
From: Helge Deller <deller@gmx.de>
Takashi Iwai 1f7d54
Date: Sat, 25 Jun 2022 13:00:34 +0200
Takashi Iwai 1f7d54
Subject: [PATCH] fbcon: Prevent that screen size is smaller than font size
Takashi Iwai 1f7d54
Git-commit: e64242caef18b4a5840b0e7a9bff37abd4f4f933
Takashi Iwai 1f7d54
Patch-mainline: v5.19-rc6
Takashi Iwai 1f7d54
References: CVE-2021-33655 bsc#1201635
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
[ backport note: the patch was heavily modified to be adaptable to the
Takashi Iwai 1f7d54
  old kernel code where both fbcon and fbmem were still separated.
Takashi Iwai 1f7d54
  The original patch exports fbcon_modechange_possible() from fbcon to
Takashi Iwai 1f7d54
  be used by fbmem, but it's not possible with the old kernel.
Takashi Iwai 1f7d54
  Hence, this patch makes fbcon_modechange_possible() to be called via
Takashi Iwai 1f7d54
  the fb notifier instead.  fbmem calls the notifier with the new event
Takashi Iwai 1f7d54
  type. -- tiwai ]
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
We need to prevent that users configure a screen size which is smaller than the
Takashi Iwai 1f7d54
currently selected font size. Otherwise rendering chars on the screen will
Takashi Iwai 1f7d54
access memory outside the graphics memory region.
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
This patch adds a new function fbcon_modechange_possible() which
Takashi Iwai 1f7d54
implements this check and which later may be extended with other checks
Takashi Iwai 1f7d54
if necessary.  The new function is called from the FBIOPUT_VSCREENINFO
Takashi Iwai 1f7d54
ioctl handler in fbmem.c, which will return -EINVAL if userspace asked
Takashi Iwai 1f7d54
for a too small screen size.
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
Signed-off-by: Helge Deller <deller@gmx.de>
Takashi Iwai 1f7d54
Reviewed-by: Geert Uytterhoeven <geert@linux-m68k.org>
Takashi Iwai 1f7d54
Cc: stable@vger.kernel.org # v5.4+
Takashi Iwai 1f7d54
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
---
Takashi Iwai 39e206
 drivers/video/console/fbcon.c    |   29 +++++++++++++++++++++++++++++
Takashi Iwai 1f7d54
 drivers/video/fbdev/core/fbmem.c |    7 ++++++-
Takashi Iwai 1f7d54
 include/linux/fb.h               |    2 ++
Takashi Iwai 39e206
 3 files changed, 37 insertions(+), 1 deletion(-)
Takashi Iwai 1f7d54
Takashi Iwai 1f7d54
--- a/drivers/video/console/fbcon.c
Takashi Iwai 1f7d54
+++ b/drivers/video/console/fbcon.c
Takashi Iwai 39e206
@@ -2676,6 +2676,32 @@ static void fbcon_set_all_vcs(struct fb_
Takashi Iwai 1f7d54
 		fbcon_modechanged(info);
Takashi Iwai 1f7d54
 }
Takashi Iwai 1f7d54
 
Takashi Iwai 1f7d54
+/* let fbcon check if it supports a new screen resolution */
Takashi Iwai 1f7d54
+static int fbcon_modechange_possible(struct fb_info *info,
Takashi Iwai 1f7d54
+				     struct fb_var_screeninfo *var)
Takashi Iwai 1f7d54
+{
Takashi Iwai 1f7d54
+	struct fbcon_ops *ops = info->fbcon_par;
Takashi Iwai 1f7d54
+	struct vc_data *vc;
Takashi Iwai 1f7d54
+	unsigned int i;
Takashi Iwai 1f7d54
+
Takashi Iwai 1f7d54
+	if (!ops)
Takashi Iwai 1f7d54
+		return 0;
Takashi Iwai 1f7d54
+
Takashi Iwai 1f7d54
+	/* prevent setting a screen size which is smaller than font size */
Takashi Iwai 1f7d54
+	for (i = first_fb_vc; i <= last_fb_vc; i++) {
Takashi Iwai 1f7d54
+		vc = vc_cons[i].d;
Takashi Iwai 1f7d54
+		if (!vc || vc->vc_mode != KD_TEXT ||
Takashi Iwai 1f7d54
+			   registered_fb[con2fb_map[i]] != info)
Takashi Iwai 1f7d54
+			continue;
Takashi Iwai 1f7d54
+
Takashi Iwai 1f7d54
+		if (vc->vc_font.width  > FBCON_SWAP(var->rotate, var->xres, var->yres) ||
Takashi Iwai 1f7d54
+		    vc->vc_font.height > FBCON_SWAP(var->rotate, var->yres, var->xres))
Takashi Iwai 1f7d54
+			return notifier_from_errno(-EINVAL);
Takashi Iwai 1f7d54
+	}
Takashi Iwai 1f7d54
+
Takashi Iwai 1f7d54
+	return 0;
Takashi Iwai 1f7d54
+}
Takashi Iwai 1f7d54
+
Takashi Iwai 1f7d54
 static int fbcon_mode_deleted(struct fb_info *info,
Takashi Iwai 1f7d54
 			      struct fb_videomode *mode)
Takashi Iwai 1f7d54
 {
Takashi Iwai 39e206
@@ -3031,6 +3057,9 @@ static int fbcon_event_notify(struct not
Takashi Iwai 1f7d54
 		idx = info->node;
Takashi Iwai 1f7d54
 		fbcon_remap_all(idx);
Takashi Iwai 1f7d54
 		break;
Takashi Iwai 1f7d54
+	case FB_EVENT_MODE_CHANGE_CHECK:
Takashi Iwai 1f7d54
+		ret = fbcon_modechange_possible(event->info, event->data);
Takashi Iwai 1f7d54
+		break;
Takashi Iwai 1f7d54
 	}
Takashi Iwai 1f7d54
 done:
Takashi Iwai 1f7d54
 	return ret;
Takashi Iwai 1f7d54
--- a/drivers/video/fbdev/core/fbmem.c
Takashi Iwai 1f7d54
+++ b/drivers/video/fbdev/core/fbmem.c
Takashi Iwai 39e206
@@ -1123,7 +1123,12 @@ static long do_fb_ioctl(struct fb_info *
Takashi Iwai 1f7d54
 			return -ENODEV;
Takashi Iwai 1f7d54
 		}
Takashi Iwai 1f7d54
 		info->flags |= FBINFO_MISC_USEREVENT;
Takashi Iwai 1f7d54
-		ret = fb_set_var(info, &var);
Takashi Iwai 1f7d54
+		event.info = info;
Takashi Iwai 1f7d54
+		event.data = &var;
Takashi Iwai 1f7d54
+		ret = fb_notifier_call_chain(FB_EVENT_MODE_CHANGE_CHECK, &event);
Takashi Iwai 1f7d54
+		ret = notifier_to_errno(ret);
Takashi Iwai 1f7d54
+		if (!ret)
Takashi Iwai 1f7d54
+			ret = fb_set_var(info, &var);
Takashi Iwai 1f7d54
 		info->flags &= ~FBINFO_MISC_USEREVENT;
Takashi Iwai 1f7d54
 		unlock_fb_info(info);
Takashi Iwai 1f7d54
 		console_unlock();
Takashi Iwai 1f7d54
--- a/include/linux/fb.h
Takashi Iwai 1f7d54
+++ b/include/linux/fb.h
Takashi Iwai 1f7d54
@@ -162,6 +162,8 @@ struct fb_cursor_user {
Takashi Iwai 1f7d54
 #define FB_EARLY_EVENT_BLANK		0x10
Takashi Iwai 1f7d54
 /*      A hardware display blank revert early change occured */
Takashi Iwai 1f7d54
 #define FB_R_EARLY_EVENT_BLANK		0x11
Takashi Iwai 1f7d54
+/* pre-check for mode change (used only by fbcon) */
Takashi Iwai 1f7d54
+#define FB_EVENT_MODE_CHANGE_CHECK	0x12
Takashi Iwai 1f7d54
 
Takashi Iwai 1f7d54
 struct fb_event {
Takashi Iwai 1f7d54
 	struct fb_info *info;