commit 58f1e0b6a4e6aa55621c6f01118994d01fd6f68c Merge: f983e445 e7bcd3cc Author: Alexander Epaneshnikov 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. --- espeak-ng-1.51.1/src/libespeak-ng/dictionary.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/dictionary.c @@ -1062,6 +1062,9 @@ 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 @@ *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); --- espeak-ng-1.51.1/src/libespeak-ng/readclause.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/readclause.c @@ -665,7 +665,7 @@ 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++] = ' '; --- espeak-ng-1.51.1/src/libespeak-ng/synthdata.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/synthdata.c @@ -75,8 +75,15 @@ 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 @@ int error = errno; fclose(f_in); free(*ptr); + *ptr = NULL; return create_file_error_context(context, error, buf); } @@ -119,9 +127,11 @@ // 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) --- espeak-ng-1.51.1/src/libespeak-ng/translate.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/translate.c @@ -2630,6 +2630,7 @@ 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); --- espeak-ng-1.51.1/src/libespeak-ng/voices.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/voices.c @@ -557,6 +557,10 @@ 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); --- espeak-ng-1.51.1/src/libespeak-ng/wavegen.c +++ espeak-ng-1.51.1_new/src/libespeak-ng/wavegen.c @@ -537,14 +537,14 @@ 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; @@ -1268,6 +1268,10 @@ 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) { --- espeak-ng-1.51.1/tests/CMakeLists.txt +++ espeak-ng-1.51.1_new/tests/CMakeLists.txt @@ -0,0 +1,78 @@ +include(CTest) + +list(APPEND _binary_tests) + +macro(compiled_test _test_name) + add_executable(test_${_test_name} + $ + ${_test_name}.c + ) + target_link_libraries(test_${_test_name} PRIVATE + $ + ) + target_compile_definitions(test_${_test_name} PRIVATE LIBESPEAK_NG_EXPORT=1) + target_include_directories( + test_${_test_name} PRIVATE + $ + $/include/compat + $ + $ + ) + if (MINGW) + target_link_options(test_${_test_name} PUBLIC "-static" "-static-libstdc++") + endif() + add_dependencies(test_${_test_name} data) + add_test( + NAME ${_test_name} + COMMAND ${ESPEAK_RUN_ENV} $ + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. + ) + list(APPEND _binary_tests test_${_test_name}) +endmacro(compiled_test) + +find_program(SHELL bash) + +macro(shell_test _test_name) + add_test( + NAME ${_test_name} + COMMAND ${ESPEAK_RUN_ENV} ESPEAK_BIN=$ ${SHELL} ${CMAKE_CURRENT_SOURCE_DIR}/${_test_name}.test + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/.. + ) +endmacro(shell_test) + +compiled_test(api) +compiled_test(encoding) +compiled_test(ieee80) +compiled_test(readclause) + +if (SHELL AND UNIX) + +shell_test(bom) +shell_test(non-executable-files-with-executable-bit) + +shell_test(cmd_options) +shell_test(dictionary) +shell_test(language-numbers-cardinal) +shell_test(language-numbers-ordinal) +shell_test(language-phonemes) +shell_test(language-pronunciation) +shell_test(language-replace) +shell_test(ssml) +shell_test(translate) +shell_test(variants) +shell_test(voices) +shell_test(crash) + +# shell_test(windows-data) +# shell_test(windows-installer) + +if (USE_KLATT) + shell_test(klatt) +endif() +if (USE_MBROLA) + shell_test(mbrola) +endif() + +endif() + +add_custom_target(tests DEPENDS ${_binary_tests}) --- espeak-ng-1.51.1/tests/crash.test +++ espeak-ng-1.51.1_new/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 --- espeak-ng-1.51.1/tests/crash_vectors/cve-2023-49990.txt +++ espeak-ng-1.51.1_new/tests/crash_vectors/cve-2023-49990.txt @@ -0,0 +1 @@ +V V Vsseeeeeeeeseee \ 文件末尾没有换行符 --- espeak-ng-1.51.1/tests/crash_vectors/cve-2023-49991.txt +++ espeak-ng-1.51.1_new/tests/crash_vectors/cve-2023-49991.txt @@ -0,0 +1 @@ +V VhVDZ컻־ִֻֻժ`v \ 文件末尾没有换行符 --- espeak-ng-1.51.1/tests/crash_vectors/cve-2023-49992.txt +++ espeak-ng-1.51.1_new/tests/crash_vectors/cve-2023-49992.txt @@ -0,0 +1 @@ +!bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbIbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb ! \ 文件末尾没有换行符 --- espeak-ng-1.51.1/tests/crash_vectors/cve-2023-49993.txt +++ espeak-ng-1.51.1_new/tests/crash_vectors/cve-2023-49993.txt @@ -0,0 +1,5 @@ +hV +$ +V +$ +B:\\lA:\@\\\H\\???T??%?\\\\\000000000000000000000000000000000000000000000000000000000@000000000000000000000000000000??0$? #??? ?-0?000000L00???\H\\???T?? ?\\\\\\u\D:\@\000L00?\\H\\???T??%?\\\\\0000000000000000200000000000000000000000000000000000000000000000000000000??0$? ? ???? ?-0?-00000L00???000E+0%!!? \ 文件末尾没有换行符 --- espeak-ng-1.51.1/tests/crash_vectors/cve-2023-49994.txt +++ espeak-ng-1.51.1_new/tests/crash_vectors/cve-2023-49994.txt @@ -0,0 +1 @@ +"[[-#,- -1-2. r--#--O)C--!E-1@5-!-V-1-- \ 文件末尾没有换行符