Bug#1059656: bookworm-pu: package espeak-ng/1.51+dfsg-10+deb12u1
Samuel Thibault
sthibault at debian.org
Fri Dec 29 20:54:57 GMT 2023
Package: release.debian.org
Severity: normal
Tags: bookworm
User: release.debian.org at packages.debian.org
Usertags: pu
X-Debbugs-Cc: espeak-ng at packages.debian.org
Control: affects -1 + src:espeak-ng
[ Reason ]
This upload provides fixes for CVEs. They are not a regression over
oldstable.
[ Impact ]
Blind users using the espeak-ng speech synthesis might be at risk when
e.g. reading a webpage that contains the CVE triggers.
[ Tests ]
CVE tests are getting added in the patch.
[ Risks ]
The code is relatively simple, comes from upstream, and has been in
testing since December 24th.
[ Checklist ]
[X] *all* changes are documented in the d/changelog
[X] I reviewed all changes and I approve them
[X] attach debdiff against the package in (old)stable
[X] the issue is verified as fixed in unstable
[ Change s]in *all* the changes)
The changes fix various use-after-free, unitialized buffers (which lead
to missing \0 terminators), and missing buffer bound checks.
-------------- next part --------------
diff -Nru espeak-ng-1.51+dfsg/debian/changelog espeak-ng-1.51+dfsg/debian/changelog
--- espeak-ng-1.51+dfsg/debian/changelog 2023-01-26 01:09:47.000000000 +0100
+++ espeak-ng-1.51+dfsg/debian/changelog 2023-12-21 01:26:02.000000000 +0100
@@ -1,3 +1,10 @@
+espeak-ng (1.51+dfsg-10+deb12u1) bookworm; urgency=medium
+
+ * patches/CVE: Fix CVE-2023-49990, CVE-2023-49991, CVE-2023-49992,
+ CVE-2023-49993, CVE-2023-49994 (Closes: Bug#1059060)
+
+ -- Samuel Thibault <sthibault at debian.org> Thu, 21 Dec 2023 01:26:02 +0100
+
espeak-ng (1.51+dfsg-10) unstable; urgency=medium
* watch: Use API instead of releases page.
diff -Nru espeak-ng-1.51+dfsg/debian/patches/CVE espeak-ng-1.51+dfsg/debian/patches/CVE
--- espeak-ng-1.51+dfsg/debian/patches/CVE 1970-01-01 01:00:00.000000000 +0100
+++ espeak-ng-1.51+dfsg/debian/patches/CVE 2023-12-21 01:26:02.000000000 +0100
@@ -0,0 +1,326 @@
+commit 58f1e0b6a4e6aa55621c6f01118994d01fd6f68c
+Merge: f983e445 e7bcd3cc
+Author: Alexander Epaneshnikov <aarnaarn2 at gmail.com>
+Date: Sun Dec 17 15:29:30 2023 +0300
+
+ tests: fix CVE crashes (#1846)
+
+ Fixes: #1823, #1824, #1825, #1826, #1827
+
+ - Add crash test and vectors provided by @SEU-SSL
+ - Disallow dummy/null voice load (that causes incorrect translator
+ initialization)
+ - Fix empty `phondata` file load (that causes unitialized memory access)
+ - Limit max word length for RemoveEnding (causes buffer overflow)
+ - Limit punctlist initialization from embedded commands (buffer
+ overflow)
+ - Fix unitialized pitch in wavegen (DBZ and indexing problems)
+ - Properly zeroize stack variables before use in TranslateClause and
+ SetWordStress
+
+ TODO (in nextup PR): add & fix more vectors from fuzzer.
+
+commit 9decedb8c229e1a4219baceaab7a3d656e889e31
+Author: Samuel Thibault <samuel.thibault at ens-lyon.org>
+Date: Thu Jun 30 00:50:18 2022 +0200
+
+ Fix missing checks for EOF
+
+commit c4c05820c4a47369d5a81e4a506fe7abb2fa7ed6
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 19:24:51 2023 +0300
+
+ tests: add CVE crash vectors
+
+commit e79405772cecf47053116aeaad10e64606292b14
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 23:55:03 2023 +0300
+
+ voices: disallow dummy voice when not compiling
+
+commit 7d4ad3c2ae063cb08bfd606021bc323dfbadaba9
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 21:50:07 2023 +0300
+
+ synthdata: fix empty file load
+
+commit b99f332c576eb49839613a55cfd3e0e1b5487191
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 22:45:15 2023 +0300
+
+ dictionary: limit word length
+
+commit 1a7ecfc2f202438b17e742368f910e6099ce02b7
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 22:50:01 2023 +0300
+
+ readclause: limit embedded punctlist length
+
+commit a5eb246debb51ba328ef399350dfcd5d87782245
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 23:03:16 2023 +0300
+
+ wavegen: fix unitialized pitch
+
+commit 5f7db763e2eff1d8174d2b65a4bbe4b2a85c8a0c
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 23:17:45 2023 +0300
+
+ translate: fix number_buf initialization
+
+commit e7bcd3cc1599ebb531bb62fc3007d3ce1dade167
+Author: Yury Popov <git at phoenix.dj>
+Date: Sat Dec 16 23:26:07 2023 +0300
+
+ dictionary: fix stack initialization
+
+---
+ src/libespeak-ng/dictionary.c | 4 ++++
+ src/libespeak-ng/readclause.c | 12 ++++++------
+ src/libespeak-ng/synthdata.c | 18 ++++++++++++++----
+ src/libespeak-ng/translate.c | 1 +
+ src/libespeak-ng/voices.c | 20 ++++++++++++--------
+ src/libespeak-ng/wavegen.c | 9 ++++++---
+ tests/crash.test | 17 +++++++++++++++++
+ tests/crash_vectors/cve-2023-49990.txt | 1 +
+ tests/crash_vectors/cve-2023-49991.txt | 1 +
+ tests/crash_vectors/cve-2023-49994.txt | 1 +
+ 10 files changed, 63 insertions(+), 21 deletions(-)
+
+--- a/src/libespeak-ng/readclause.c
++++ b/src/libespeak-ng/readclause.c
+@@ -335,7 +335,7 @@ static int AnnouncePunctuation(Translato
+
+ if ((*bufix == 0) || (end_clause == 0) || (tr->langopts.param[LOPT_ANNOUNCE_PUNCT] & 2)) {
+ punct_count = 1;
+- while ((c2 == c1) && (c1 != '<')) { // don't eat extra '<', it can miss XML tags
++ while (!Eof() && (c2 == c1) && (c1 != '<')) { // don't eat extra '<', it can miss XML tags
+ punct_count++;
+ c2 = GetC();
+ }
+@@ -647,7 +647,7 @@ int ReadClause(Translator *tr, char *buf
+ // an embedded command. If it's a voice change, end the clause
+ if (c2 == 'V') {
+ buf[ix++] = 0; // end the clause at this point
+- while (!iswspace(c1 = GetC()) && !Eof() && (ix < (n_buf-1)))
++ while (!Eof() && !iswspace(c1 = GetC()) && (ix < (n_buf-1)))
+ buf[ix++] = c1; // add voice name to end of buffer, after the text
+ buf[ix++] = 0;
+ return CLAUSE_VOICE;
+@@ -657,7 +657,7 @@ int ReadClause(Translator *tr, char *buf
+ strcpy(&buf[ix], " ");
+ ix += 3;
+
+- if ((c2 = GetC()) == '0')
++ if (!Eof() && (c2 = GetC()) == '0')
+ option_punctuation = 0;
+ else {
+ option_punctuation = 1;
+@@ -665,7 +665,7 @@ int ReadClause(Translator *tr, char *buf
+ if (c2 != '1') {
+ // a list of punctuation characters to be spoken, terminated by space
+ j = 0;
+- while (!iswspace(c2) && !Eof()) {
++ while (!Eof() && !iswspace(c2) && (j < N_PUNCTLIST-1)) {
+ option_punctlist[j++] = c2;
+ c2 = GetC();
+ buf[ix++] = ' ';
+@@ -791,7 +791,7 @@ int ReadClause(Translator *tr, char *buf
+ }
+
+ if ((c1 == '.') && (c2 == '.')) {
+- while ((c_next = GetC()) == '.') {
++ while (!Eof() && (c_next = GetC()) == '.') {
+ // 3 or more dots, replace by elipsis
+ c1 = 0x2026;
+ c2 = ' ';
+@@ -808,7 +808,7 @@ int ReadClause(Translator *tr, char *buf
+ // Handling of sequences of ? and ! like ??!?, !!??!, ?!! etc
+ // Use only first char as determinant
+ if(punct_data & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
+- while(clause_type_from_codepoint(c2) & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
++ while(!Eof() && clause_type_from_codepoint(c2) & (CLAUSE_QUESTION | CLAUSE_EXCLAMATION)) {
+ c_next = GetC();
+ c2 = c_next;
+ }
+--- /dev/null
++++ b/tests/crash.test
+@@ -0,0 +1,17 @@
++#!/bin/sh
++# include common script
++. "`dirname $0`/common"
++
++test_crash() {
++ TEST_NAME=$1
++
++ echo "testing CVE-${TEST_NAME}"
++ ESPEAK_DATA_PATH=`pwd` LD_LIBRARY_PATH=src:${LD_LIBRARY_PATH} \
++ $VALGRIND src/espeak-ng -f "$(dirname $0)/crash_vectors/${TEST_NAME}.txt" -w /dev/null || exit 1
++}
++
++test_crash cve-2023-49990
++test_crash cve-2023-49991
++test_crash cve-2023-49992
++test_crash cve-2023-49993
++test_crash cve-2023-49994
+--- /dev/null
++++ b/tests/crash_vectors/cve-2023-49990.txt
+@@ -0,0 +1 @@
++??V??
??V
??V??????s????????????s????????eeeeeeeeseee?????
+\ No newline at end of file
+--- /dev/null
++++ b/tests/crash_vectors/cve-2023-49991.txt
+@@ -0,0 +1 @@
++??V?
??V??h?????VD?Z?????????????????????????????????????????????`v
+\ No newline at end of file
+--- /dev/null
++++ b/tests/crash_vectors/cve-2023-49994.txt
+@@ -0,0 +1 @@
++"[[-#,- -1-2.
r--?#--O)C--!?E-1?@5-!-V-1--
+\ No newline at end of file
+--- a/src/libespeak-ng/voices.c
++++ b/src/libespeak-ng/voices.c
+@@ -554,6 +554,10 @@ voice_t *LoadVoice(const char *vname, in
+ static char voice_name[40]; // voice name for current_voice_selected
+ static char voice_languages[100]; // list of languages and priorities for current_voice_selected
+
++ if ((vname == NULL || vname[0] == 0) && !(control & 8)) {
++ return NULL;
++ }
++
+ strncpy0(voicename, vname, sizeof(voicename));
+ if (control & 0x10) {
+ strcpy(buf, vname);
+@@ -937,14 +941,14 @@ voice_t *LoadVoice(const char *vname, in
+
+ if (!tone_only) {
+ if (!!(control & 8/*compiling phonemes*/)) {
+- /* Set by espeak_ng_CompilePhonemeDataPath when it
+- * calls LoadVoice("", 8) to set up a dummy(?) voice.
+- * As phontab may not yet exist this avoids the spurious
+- * error message and guarantees consistent results by
+- * not actually reading a potentially bogus phontab...
+- */
+- ix = 0;
+- } else if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) {
++ /* Set by espeak_ng_CompilePhonemeDataPath when it
++ * calls LoadVoice("", 8) to set up a dummy(?) voice.
++ * As phontab may not yet exist this avoids the spurious
++ * error message and guarantees consistent results by
++ * not actually reading a potentially bogus phontab...
++ */
++ ix = 0;
++ } else if ((ix = SelectPhonemeTableName(phonemes_name)) < 0) {
+ fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name);
+ ix = 0;
+ }
+--- a/src/libespeak-ng/synthdata.c
++++ b/src/libespeak-ng/synthdata.c
+@@ -75,8 +75,15 @@ static espeak_ng_STATUS ReadPhFile(void
+ if ((f_in = fopen(buf, "rb")) == NULL)
+ return create_file_error_context(context, errno, buf);
+
+- if (*ptr != NULL)
++ if (*ptr != NULL) {
+ free(*ptr);
++ *ptr = NULL;
++ }
++
++ if (length == 0) {
++ *ptr = NULL;
++ return 0;
++ }
+
+ if ((*ptr = malloc(length)) == NULL) {
+ fclose(f_in);
+@@ -86,6 +93,7 @@ static espeak_ng_STATUS ReadPhFile(void
+ int error = errno;
+ fclose(f_in);
+ free(*ptr);
++ *ptr = NULL;
+ return create_file_error_context(context, error, buf);
+ }
+
+@@ -119,9 +127,11 @@ espeak_ng_STATUS LoadPhData(int *srate,
+ // read the version number and sample rate from the first 8 bytes of phondata
+ version = 0; // bytes 0-3, version number
+ rate = 0; // bytes 4-7, sample rate
+- for (ix = 0; ix < 4; ix++) {
+- version += (wavefile_data[ix] << (ix*8));
+- rate += (wavefile_data[ix+4] << (ix*8));
++ if (wavefile_data) {
++ for (ix = 0; ix < 4; ix++) {
++ version += (wavefile_data[ix] << (ix*8));
++ rate += (wavefile_data[ix+4] << (ix*8));
++ }
+ }
+
+ if (version != version_phdata)
+--- a/src/libespeak-ng/dictionary.c
++++ b/src/libespeak-ng/dictionary.c
+@@ -1062,6 +1062,9 @@ void SetWordStress(Translator *tr, char
+
+ static char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 };
+
++ memset(syllable_weight, 0, sizeof(syllable_weight));
++ memset(vowel_length, 0, sizeof(vowel_length));
++
+ stressflags = tr->langopts.stress_flags;
+
+ if (dictionary_flags != NULL)
+@@ -3070,6 +3073,7 @@ int RemoveEnding(Translator *tr, char *w
+ *word_end = 'e';
+ }
+ i = word_end - word;
++ if (i >= N_WORD_BYTES) i = N_WORD_BYTES-1;
+
+ if (word_copy != NULL) {
+ memcpy(word_copy, word, i);
+--- a/src/libespeak-ng/wavegen.c
++++ b/src/libespeak-ng/wavegen.c
+@@ -537,14 +537,14 @@ static void AdvanceParameters()
+ if (wvoice == NULL)
+ return;
+
+- int x;
++ int x = 0;
+ int ix;
+ static int Flutter_ix = 0;
+
+ // advance the pitch
+ wdata.pitch_ix += wdata.pitch_inc;
+ if ((ix = wdata.pitch_ix>>8) > 127) ix = 127;
+- x = wdata.pitch_env[ix] * wdata.pitch_range;
++ if (wdata.pitch_env) x = wdata.pitch_env[ix] * wdata.pitch_range;
+ wdata.pitch = (x>>8) + wdata.pitch_base;
+
+
+@@ -560,7 +560,7 @@ static void AdvanceParameters()
+
+ if(const_f0)
+ wdata.pitch = (const_f0<<12);
+-
++
+ if (wdata.pitch < 102400)
+ wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
+
+@@ -1268,6 +1268,9 @@ static int WavegenFill2()
+ static bool resume = false;
+ static int echo_complete = 0;
+
++ if (wdata.pitch < 102400)
++ wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)
++
+ while (out_ptr < out_end) {
+ if (WcmdqUsed() <= 0) {
+ if (echo_complete > 0) {
+--- a/src/libespeak-ng/translate.c
++++ b/src/libespeak-ng/translate.c
+@@ -2630,6 +2630,7 @@ void TranslateClause(Translator *tr, int
+ if (dict_flags & FLAG_SPELLWORD) {
+ // redo the word, speaking single letters
+ for (pw = word; *pw != ' ';) {
++ memset(number_buf, 0, sizeof(number_buf));
+ memset(number_buf, ' ', 9);
+ nx = utf8_in(&c_temp, pw);
+ memcpy(&number_buf[2], pw, nx);
diff -Nru espeak-ng-1.51+dfsg/debian/patches/series espeak-ng-1.51+dfsg/debian/patches/series
--- espeak-ng-1.51+dfsg/debian/patches/series 2022-09-23 00:13:40.000000000 +0200
+++ espeak-ng-1.51+dfsg/debian/patches/series 2023-12-21 01:26:02.000000000 +0100
@@ -9,3 +9,4 @@
lang
long-build-path
mb-fr
+CVE
diff -Nru espeak-ng-1.51+dfsg/debian/salsa-ci.yml espeak-ng-1.51+dfsg/debian/salsa-ci.yml
--- espeak-ng-1.51+dfsg/debian/salsa-ci.yml 2022-09-23 01:53:17.000000000 +0200
+++ espeak-ng-1.51+dfsg/debian/salsa-ci.yml 2023-12-21 01:26:02.000000000 +0100
@@ -5,6 +5,8 @@
# needs building a host espeak-ng to generate the phoneme databases
variables:
+ RELEASE: bookworm
+
SALSA_CI_DISABLE_CROSSBUILD_ARM64: '1'
SALSA_CI_REPROTEST_ENABLE_DIFFOSCOPE: 1
More information about the Pkg-a11y-devel
mailing list