[PATCH 09/13] lib/fonts: Compare font data for equality with font_data_is_equal()

From: Thomas Zimmermann

Date: Wed Feb 18 2026 - 03:42:56 EST


Add font_data_is_equal() and update consoles to use it.

Font data is equal if it has the same size and contains the same values
on all bytes. Only fbcon uses a crc32 checksum. If set in both operands
the checksums have to be equal.

The new helper also guarantees to not compare internal fonts against
fonts from user space. Internal fonts cannot be ref-counted, so making
them equal to user-space fonts with the same byte sequence results in
undefined behavior.

The test only compares data buffers. Their interpretation is up each
console. Therefore remove a width test in fbcon_set_font().

Signed-off-by: Thomas Zimmermann <tzimmermann@xxxxxxx>
---
drivers/video/console/newport_con.c | 3 +--
drivers/video/fbdev/core/fbcon.c | 7 +-----
include/linux/font.h | 1 +
lib/fonts/fonts.c | 37 +++++++++++++++++++++++++++--
4 files changed, 38 insertions(+), 10 deletions(-)

diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index eee9695c3eb5..02bf4df05016 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -530,8 +530,7 @@ static int newport_set_font(int unit, const struct console_font *op,
/* check if font is already used by other console */
for (i = 0; i < MAX_NR_CONSOLES; i++) {
if (font_data[i] != FONT_DATA
- && font_data_size(font_data[i]) == size
- && !memcmp(font_data[i], new_data, size)) {
+ && font_data_is_equal(font_data[i], new_data)) {
kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
/* current font is the same as the new one */
if (i == unit)
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index ebb9c5c1b247..6fbecce606fd 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2553,14 +2553,9 @@ static int fbcon_set_font(struct vc_data *vc, const struct console_font *font,
FNTSUM(new_data) = csum;
/* Check if the same font is on some other console already */
for (i = first_fb_vc; i <= last_fb_vc; i++) {
- struct vc_data *tmp = vc_cons[i].d;
-
if (fb_display[i].userfont &&
fb_display[i].fontdata &&
- FNTSUM(fb_display[i].fontdata) == csum &&
- font_data_size(fb_display[i].fontdata) == size &&
- tmp->vc_font.width == w &&
- !memcmp(fb_display[i].fontdata, new_data, size)) {
+ font_data_is_equal(fb_display[i].fontdata, new_data)) {
kfree(new_data - FONT_EXTRA_WORDS * sizeof(int));
new_data = (u8 *)fb_display[i].fontdata;
break;
diff --git a/include/linux/font.h b/include/linux/font.h
index 3afb32b625d3..da9869ca2294 100644
--- a/include/linux/font.h
+++ b/include/linux/font.h
@@ -55,6 +55,7 @@ static inline const unsigned char *font_data_buf(font_data_t *fd)
}

unsigned int font_data_size(font_data_t *fd);
+bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs);

/*
* Font lookup
diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c
index 8c9a6762061c..c9f6328d5dda 100644
--- a/lib/fonts/fonts.c
+++ b/lib/fonts/fonts.c
@@ -12,18 +12,25 @@
* for more details.
*/

+#include <linux/font.h>
#include <linux/module.h>
-#include <linux/types.h>
#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/sections.h>
#if defined(__mc68000__)
#include <asm/setup.h>
#endif
-#include <linux/font.h>

/*
* Helpers for font_data_t
*/

+static bool font_data_is_internal(font_data_t *fd)
+{
+ return is_kernel_rodata((unsigned long)fd);
+}
+
/**
* font_data_size - Return size of the font data in bytes
* @fd: Font data
@@ -37,6 +44,32 @@ unsigned int font_data_size(font_data_t *fd)
}
EXPORT_SYMBOL_GPL(font_data_size);

+/**
+ * font_data_is_equal - Compares font data for equality
+ * @lhs: Left-hand side font data
+ * @rhs: Right-hand-size font data
+ *
+ * Font data is equal if is constain the same sequence of values. The
+ * helper also use the checksum, if both arguments contain it. Font data
+ * coming from different origins, internal or from user space, is never
+ * equal. Allowing this would break reference counting.
+ *
+ * Returns:
+ * True if the given font data is equal, false otherwise.
+ */
+bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs)
+{
+ if (font_data_is_internal(lhs) != font_data_is_internal(rhs))
+ return false;
+ if (font_data_size(lhs) != font_data_size(rhs))
+ return false;
+ if (FNTSUM(lhs) && FNTSUM(rhs) && FNTSUM(lhs) != FNTSUM(rhs))
+ return false;
+
+ return !memcmp(lhs, rhs, FNTSIZE(lhs));
+}
+EXPORT_SYMBOL_GPL(font_data_is_equal);
+
/*
* Font lookup
*/
--
2.52.0