Browse Source

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.
master
Alexander Epaneshnikov 1 year ago
parent
commit
58f1e0b6a4
No account linked to committer's email address

+ 4
- 0
src/libespeak-ng/dictionary.c View File



static const char consonant_types[16] = { 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }; static const 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; stressflags = tr->langopts.stress_flags;


if (dictionary_flags != NULL) if (dictionary_flags != NULL)
*word_end = 'e'; *word_end = 'e';
} }
i = word_end - word; i = word_end - word;
if (i >= N_WORD_BYTES) i = N_WORD_BYTES-1;


if (word_copy != NULL) { if (word_copy != NULL) {
memcpy(word_copy, word, i); memcpy(word_copy, word, i);

+ 1
- 1
src/libespeak-ng/readclause.c View File

if (c2 != '1') { if (c2 != '1') {
// a list of punctuation characters to be spoken, terminated by space // a list of punctuation characters to be spoken, terminated by space
j = 0; j = 0;
while (!Eof() && !iswspace(c2)) {
while (!Eof() && !iswspace(c2) && (j < N_PUNCTLIST-1)) {
option_punctlist[j++] = c2; option_punctlist[j++] = c2;
c2 = GetC(); c2 = GetC();
buf[ix++] = ' '; buf[ix++] = ' ';

+ 14
- 4
src/libespeak-ng/synthdata.c View File

if ((f_in = fopen(buf, "rb")) == NULL) if ((f_in = fopen(buf, "rb")) == NULL)
return create_file_error_context(context, errno, buf); return create_file_error_context(context, errno, buf);


if (*ptr != NULL)
if (*ptr != NULL) {
free(*ptr); free(*ptr);
*ptr = NULL;
}
if (length == 0) {
*ptr = NULL;
return 0;
}


if ((*ptr = malloc(length)) == NULL) { if ((*ptr = malloc(length)) == NULL) {
fclose(f_in); fclose(f_in);
int error = errno; int error = errno;
fclose(f_in); fclose(f_in);
free(*ptr); free(*ptr);
*ptr = NULL;
return create_file_error_context(context, error, buf); return create_file_error_context(context, error, buf);
} }


// read the version number and sample rate from the first 8 bytes of phondata // read the version number and sample rate from the first 8 bytes of phondata
version = 0; // bytes 0-3, version number version = 0; // bytes 0-3, version number
rate = 0; // bytes 4-7, sample rate 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) if (version != version_phdata)

+ 1
- 0
src/libespeak-ng/translate.c View File

if (dict_flags & FLAG_SPELLWORD) { if (dict_flags & FLAG_SPELLWORD) {
// redo the word, speaking single letters // redo the word, speaking single letters
for (pw = word; *pw != ' ';) { for (pw = word; *pw != ' ';) {
memset(number_buf, 0, sizeof(number_buf));
memset(number_buf, ' ', 9); memset(number_buf, ' ', 9);
nx = utf8_in(&c_temp, pw); nx = utf8_in(&c_temp, pw);
memcpy(&number_buf[2], pw, nx); memcpy(&number_buf[2], pw, nx);

+ 12
- 8
src/libespeak-ng/voices.c View File

MAKE_MEM_UNDEFINED(&voice_languages, sizeof(voice_languages)); MAKE_MEM_UNDEFINED(&voice_languages, sizeof(voice_languages));
} }


if ((vname == NULL || vname[0] == 0) && !(control & 8)) {
return NULL;
}

strncpy0(voicename, vname, sizeof(voicename)); strncpy0(voicename, vname, sizeof(voicename));
if (control & 0x10) { if (control & 0x10) {
strcpy(buf, vname); strcpy(buf, vname);


if (!tone_only) { if (!tone_only) {
if (!!(control & 8/*compiling phonemes*/)) { 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); fprintf(stderr, "Unknown phoneme table: '%s'\n", phonemes_name);
ix = 0; ix = 0;
} }

+ 6
- 3
src/libespeak-ng/wavegen.c View File

if (wvoice == NULL) if (wvoice == NULL)
return; return;


int x;
int x = 0;
int ix; int ix;
static int Flutter_ix = 0; static int Flutter_ix = 0;


// advance the pitch // advance the pitch
wdata.pitch_ix += wdata.pitch_inc; wdata.pitch_ix += wdata.pitch_inc;
if ((ix = wdata.pitch_ix>>8) > 127) ix = 127; 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; wdata.pitch = (x>>8) + wdata.pitch_base;
if(const_f0) if(const_f0)
wdata.pitch = (const_f0<<12); wdata.pitch = (const_f0<<12);
if (wdata.pitch < 102400) if (wdata.pitch < 102400)
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12) wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)


static bool resume = false; static bool resume = false;
static int echo_complete = 0; static int echo_complete = 0;


if (wdata.pitch < 102400)
wdata.pitch = 102400; // min pitch, 25 Hz (25 << 12)

while (out_ptr < out_end) { while (out_ptr < out_end) {
if (WcmdqUsed() <= 0) { if (WcmdqUsed() <= 0) {
if (echo_complete > 0) { if (echo_complete > 0) {

+ 1
- 0
tests/CMakeLists.txt View File

shell_test(translate) shell_test(translate)
shell_test(variants) shell_test(variants)
shell_test(voices) shell_test(voices)
shell_test(crash)


# shell_test(windows-data) # shell_test(windows-data)
# shell_test(windows-installer) # shell_test(windows-installer)

+ 17
- 0
tests/crash.test View File

#!/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

+ 1
- 0
tests/crash_vectors/cve-2023-49990.txt View File

ã¦à»Vñ€¦ñ €¦V €äVñ€ãÂà¦æsññâñþâññà¶æØØsññâñþâññeeeeeeeeseee€ññûñ

+ 1
- 0
tests/crash_vectors/cve-2023-49991.txt View File

€¦Vń €ńVđŐhńůâ˙ńVDíZ»»ŐöÖÖÖÖÖÖÖÖÖě»»ş»ÖľÖÖÖÖÖÖ´ÖÖÖ»ţţ÷ÜÖÖÖ»»ş»ŐŞ»»®î˙˙€ę`v

BIN
tests/crash_vectors/cve-2023-49992.txt View File


BIN
tests/crash_vectors/cve-2023-49993.txt View File


+ 1
- 0
tests/crash_vectors/cve-2023-49994.txt View File

"[[-#,- -1-2. r--Ş#--O)C--!˙E-1‹@5-!-V-1--

Loading…
Cancel
Save