From c0ae6c7cd1b0d6ae59d2e99cd684b7e0962037f5 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Tue, 29 Sep 2026 20:22:45 -0400 Subject: [PATCH] FS#14019: Detect and reject outdated binary language files (WIP) Accomplish this by checksumming the english language input and (1) including that in binary files and (2) checking the value matches what was compiled into the firmware image Not sure if this is the best approach but it works. Change-Id: I8f79ad1b9d1cdf69e6a085d7b3dd1b5e078af04b --- apps/lang/lang.make | 6 +++--- apps/language.c | 9 +++++---- tools/genlang | 24 ++++++++++++++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/apps/lang/lang.make b/apps/lang/lang.make index 5923597e86..405444e022 100644 --- a/apps/lang/lang.make +++ b/apps/lang/lang.make @@ -20,18 +20,18 @@ CLEANOBJS += $(BUILDDIR)/lang/max_language_size.h $(BUILDDIR)/lang/lang* #DUMMY := $(shell mkdir -p $(BUILDDIR)/apps/lang) # Calculate the maximum language size. Currently based on the file size -# of the largest lng file. Subtract 10 due to HEADER_SIZE and +# of the largest lng file. Subtract 11 due to HEADER_SIZE and # SUBHEADER_SIZE. # TODO: In the future generate this file within genlang or another script # in order to only calculate the maximum size based on the core strings. $(BUILDDIR)/lang/max_language_size.h: $(LANGOBJ) $(BUILDDIR)/apps/lang/voicestrings.zip $(call PRINTS,GEN $(subst $(BUILDDIR)/,,$@)) - $(SILENT)echo "#define MAX_LANGUAGE_SIZE `ls -ln $(BUILDDIR)/apps/lang/*.lng | awk '{print $$5-10}' | sort -n | tail -1`" > $@ + $(SILENT)echo "#define MAX_LANGUAGE_SIZE `ls -ln $(BUILDDIR)/apps/lang/*.lng | awk '{print $$5-11}' | sort -n | tail -1`" > $@ $(BUILDDIR)/lang/lang_core.o: $(BUILDDIR)/lang/lang.h $(BUILDDIR)/lang/lang_core.c $(call PRINTS,CC lang_core.c)$(CC) $(CFLAGS) -c $(BUILDDIR)/lang/lang_core.c -o $@ -# genlang creates *both* lang.c and lang.h but in Make there is no wat to express this rule +# genlang creates *both* lang.c and lang.h but in Make there is no way to express this rule # (multiple target rules DO NOT express that, they are a simple shortcut for multiple rules) # instead we pretend that genlang create lang_core.c and that lang.c depends from lang.h # it will work fine as long as one never manually removes lang.c and not lang.h, and it will avoid diff --git a/apps/language.c b/apps/language.c index 096c708e62..d63aea6021 100644 --- a/apps/language.c +++ b/apps/language.c @@ -35,12 +35,12 @@ /* These defines must match the initial bytes in the binary lang file */ /* See tools/genlang (TODO: Use common include for both) */ #define LANGUAGE_COOKIE 0x1a -#define LANGUAGE_VERSION 0x06 +#define LANGUAGE_VERSION 0x07 #define LANGUAGE_FLAG_RTL 0x01 #define LANGUAGE_FLAG_UNITS_FIRST 0x02 -#define HEADER_SIZE 4 +#define HEADER_SIZE 5 #define SUBHEADER_SIZE 6 static unsigned char language_buffer[MAX_LANGUAGE_SIZE]; @@ -72,8 +72,9 @@ int lang_load(const char *filename, const unsigned char *builtin, if(read(fd, lang_header, HEADER_SIZE) == HEADER_SIZE && ((lang_header[0] == LANGUAGE_COOKIE) && - (lang_header[1] == LANGUAGE_VERSION) && - (lang_header[2] == TARGET_ID))) { + (lang_header[1] == LANGUAGE_VERSION) && + (lang_header[2] == TARGET_ID) && + (lang_header[4] == ENGLISH_LANG_CHECKSUM)) ) { /* jump to the proper entry in the table of subheaders */ lseek(fd, user_num * SUBHEADER_SIZE, SEEK_CUR); if (read(fd, sub_header, SUBHEADER_SIZE) != SUBHEADER_SIZE) diff --git a/tools/genlang b/tools/genlang index 3cd470d0a1..c6b7645677 100755 --- a/tools/genlang +++ b/tools/genlang @@ -9,11 +9,14 @@ # Copyright (C) 2006 - 2008 by Daniel Stenberg # +use Digest::SHA1; + # See apps/language.c (TODO: Use common include for both) # Cookie and binary version for the binary lang file my $LANGUAGE_COOKIE = 0x1a; +my $LANGUAGE_VERSION = 0x07; my $VOICE_COOKIE = 0x9a; -my $LANGUAGE_VERSION = 0x06; +my $VOICE_VERSION = 0x06; my $LANGUAGE_FLAG_RTL = 0x01; my $LANGUAGE_FLAG_UNITS_FIRST = 0x02; @@ -70,6 +73,16 @@ if (!$english) { print STDERR "Please specify the english lang source (with -e)!\n"; exit; } +my $english_crc; +{ + open my $fh, "<$english" or die $!; + binmode $fh; + my $sha = Digest::SHA1->new; + $sha->addfile($fh); + $english_crc = $sha->hexdigest; + $english_crc = substr($english_crc, 0, 2); + close $fh; +} my $target_id = $i; if($binary && !$target_id) { @@ -501,6 +514,9 @@ extern unsigned char *language_strings[]; /* this contains the concatenation of all strings, separated by \\0 chars */ extern const unsigned char core_language_builtin[]; +/* this is a checksum of the english language file */ +#define ENGLISH_LANG_CHECKSUM 0x$english_crc + #include "${prefix}_enum.h" MOO @@ -604,13 +620,13 @@ elsif($binary || $binvoice) { if($binary) { open(OUTF, ">$binary") or die "Error: Can't create $binary"; binmode OUTF; - printf OUTF ("%c%c%c%c", $LANGUAGE_COOKIE, $LANGUAGE_VERSION, $target_id, - $langoptions); # magic lang file header + printf OUTF ("%c%c%c%c%c", $LANGUAGE_COOKIE, $LANGUAGE_VERSION, $target_id, + $langoptions, hex($english_crc)); # magic lang file header } if($binvoice) { open(OUTV, ">$binvoice") or die "Error: Can't create $binary"; binmode OUTV; - printf OUTV ("%c%c%c%c", $VOICE_COOKIE, $LANGUAGE_VERSION, $target_id, + printf OUTV ("%c%c%c%c", $VOICE_COOKIE, $VOICE_VERSION, $target_id, $langoptions); # magic lang file header }