espeak-ng/CVE-2023-49990~CVE-2023-49994.patch

329 lines
11 KiB
Diff
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Origin: https://sources.debian.org/data/main/e/espeak-ng/1.51%2Bdfsg-12/debian/patches/CVE
commit 58f1e0b6a4e6aa55621c6f01118994d01fd6f68c
Merge: f983e445 e7bcd3cc
Author: Alexander Epaneshnikov <aarnaarn2@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@ens-lyon.org>
Date: Thu Jun 30 00:50:18 2022 +0200
Fix missing checks for EOF
commit c4c05820c4a47369d5a81e4a506fe7abb2fa7ed6
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 19:24:51 2023 +0300
tests: add CVE crash vectors
commit e79405772cecf47053116aeaad10e64606292b14
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 23:55:03 2023 +0300
voices: disallow dummy voice when not compiling
commit 7d4ad3c2ae063cb08bfd606021bc323dfbadaba9
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 21:50:07 2023 +0300
synthdata: fix empty file load
commit b99f332c576eb49839613a55cfd3e0e1b5487191
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 22:45:15 2023 +0300
dictionary: limit word length
commit 1a7ecfc2f202438b17e742368f910e6099ce02b7
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 22:50:01 2023 +0300
readclause: limit embedded punctlist length
commit a5eb246debb51ba328ef399350dfcd5d87782245
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 23:03:16 2023 +0300
wavegen: fix unitialized pitch
commit 5f7db763e2eff1d8174d2b65a4bbe4b2a85c8a0c
Author: Yury Popov <git@phoenix.dj>
Date: Sat Dec 16 23:17:45 2023 +0300
translate: fix number_buf initialization
commit e7bcd3cc1599ebb531bb62fc3007d3ce1dade167
Author: Yury Popov <git@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 @@
+<2B><><EFBFBD><EFBFBD>V<><56><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD>V
<EFBFBD><EFBFBD>V<><07><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>s<><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>s<EFBFBD><73><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>eeeeeeeeseee<65><65><EFBFBD><EFBFBD><EFBFBD>
\ No newline at end of file
--- /dev/null
+++ b/tests/crash_vectors/cve-2023-49991.txt
@@ -0,0 +1 @@
+<2B><>V<>
<EFBFBD><EFBFBD>V<><56>h<EFBFBD><68><EFBFBD><EFBFBD><EFBFBD>VD<56>Z<EFBFBD><5A><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><ECBBBB>־<EFBFBD><D6BE><EFBFBD><EFBFBD><EFBFBD>ִ<EFBFBD><D6B4>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD><08><>ֻ<EFBFBD><D6BB><EFBFBD>ժ<EFBFBD><D5AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>`v
\ No newline at end of file
--- /dev/null
+++ b/tests/crash_vectors/cve-2023-49994.txt
@@ -0,0 +1 @@
+"[[-#,- -1-2.
r--<2D>#--O)C--!<21>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);