diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee1b25a0..4a806361c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,7 +139,7 @@ jobs: - name: "clang" cc: "clang" cxx: "clang++" - buildargs: "" + buildargs: "-DENABLE_FUZZ=ON" name: "${{ matrix.os }} (${{ matrix.config.name }})" runs-on: ${{ matrix.os }} @@ -305,7 +305,7 @@ jobs: - name: "clang" cc: "clang" cxx: "clang++" - buildargs: "-DENABLE_MAN=ON -DENABLE_DOC=ON -DENABLE_TESTS=ON" + buildargs: "-DENABLE_MAN=ON -DENABLE_DOC=ON -DENABLE_TESTS=ON -DENABLE_FUZZ=ON" name: "rockylinux-9 (${{ matrix.config.name }})" runs-on: ${{ matrix.os }} @@ -354,7 +354,7 @@ jobs: - name: "clang" cc: "clang" cxx: "clang++" - buildargs: "-DENABLE_MAN=ON -DENABLE_DOC=ON -DENABLE_TESTS=ON" + buildargs: "-DENABLE_MAN=ON -DENABLE_DOC=ON -DENABLE_TESTS=ON -DENABLE_FUZZ=ON" name: "freebsd-14 (${{ matrix.config.name }})" runs-on: ${{ matrix.os }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f658c9bc..6aa6e3b0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ SPDX-License-Identifier: GPL-3.0-or-later - irc: add support strikethrough text in IRC messages ([#2248](https://github.com/weechat/weechat/issues/2248)) - buflist: add variables `${number_zero}` and `${number_zero2}` (zero-padded buffer number) +- tests: add fuzz testing ([#1462](https://github.com/weechat/weechat/issues/1462)) ### Fixed diff --git a/CMakeLists.txt b/CMakeLists.txt index ba0ccbef4..685b27998 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,6 +131,7 @@ option(ENABLE_MAN "Enable build of man page" OFF) option(ENABLE_DOC "Enable build of documentation" OFF) option(ENABLE_DOC_INCOMPLETE "Enable incomplete doc" OFF) option(ENABLE_TESTS "Enable tests" OFF) +option(ENABLE_FUZZ "Enable fuzz testing" OFF) option(ENABLE_CODE_COVERAGE "Enable code coverage" OFF) # code coverage @@ -276,19 +277,7 @@ endif() add_subdirectory(src) add_subdirectory(doc) - -if(ENABLE_TESTS) - find_package(CppUTest) - if(CPPUTEST_FOUND) - enable_testing() - add_subdirectory(tests) - else() - message(SEND_ERROR "CppUTest not found") - endif() -else() - enable_testing() - add_test(NAME notests COMMAND true) -endif() +add_subdirectory(tests) configure_file(config.h.cmake config.h @ONLY) diff --git a/doc/en/weechat_dev.en.adoc b/doc/en/weechat_dev.en.adoc index 4bcb9558c..98f428b27 100644 --- a/doc/en/weechat_dev.en.adoc +++ b/doc/en/weechat_dev.en.adoc @@ -91,6 +91,8 @@ The main WeeChat directories are: |       typing/ | Typing plugin. |       xfer/ | Xfer plugin (IRC DCC file/chat). | tests/ | Tests. +|    fuzz/ | Fuzz testing. +|       core/ | Fuzz testing for core functions. |    unit/ | Unit tests. |       core/ | Unit tests for core functions. |          hook/ | Unit tests for hook functions. @@ -415,6 +417,12 @@ WeeChat "core" is located in following directories: |=== | Path/file | Description | tests/ | Root of tests. +|    fuzz/ | Root of fuzz testing. +|       core/ | Root of fuzz testing for core. +|          calc-fuzzer.c | Fuzz testing: calculation of expressions. +|          crypto-fuzzer.c | Fuzz testing: cryptographic functions. +|          string-fuzzer.c | Fuzz testing: strings. +|          utf8-fuzzer.c | Fuzz testing: UTF-8. |    unit/ | Root of unit tests. |       tests.cpp | Program used to run all tests. |       tests-record.cpp | Record and search in messages displayed. diff --git a/doc/fr/weechat_dev.fr.adoc b/doc/fr/weechat_dev.fr.adoc index 41f8b180a..5c32ea0b6 100644 --- a/doc/fr/weechat_dev.fr.adoc +++ b/doc/fr/weechat_dev.fr.adoc @@ -92,6 +92,8 @@ Les répertoires principaux de WeeChat sont : |       typing/ | Extension Typing. |       xfer/ | Extension Xfer (IRC DCC fichier/discussion). | tests/ | Tests. +|    fuzz/ | Fuzzing (tests à données aléatoires). +|       core/ | Fuzzing pour les fonctions du cœur. |    unit/ | Tests unitaires. |       core/ | Tests unitaires pour les fonctions du cœur. |          hook/ | Tests unitaires pour les fonctions hook. @@ -416,6 +418,12 @@ Le cœur de WeeChat est situé dans les répertoires suivants : |=== | Chemin/fichier | Description | tests/ | Racine des tests. +|    fuzz/ | Racine du fuzzing (tests à données aléatoires). +|       core/ | Racine du fuzzing pour le cœur. +|          calc-fuzzer.c | Fuzzing : calcul d'expressions. +|          crypto-fuzzer.c | Fuzzing : fonctions cryptographiques. +|          string-fuzzer.c | Fuzzing : chaînes. +|          utf8-fuzzer.c | Fuzzing : UTF-8. |    unit/ | Racine des tests unitaires. |       tests.cpp | Programme utilisé pour lancer tous les tests. |       tests-record.cpp | Enregistrement et recherche dans les messages affichés. diff --git a/doc/ja/weechat_dev.ja.adoc b/doc/ja/weechat_dev.ja.adoc index 661356ccd..fdba82bd1 100644 --- a/doc/ja/weechat_dev.ja.adoc +++ b/doc/ja/weechat_dev.ja.adoc @@ -93,6 +93,10 @@ qweechat:: |       typing/ | typing プラグイン |       xfer/ | xfer (IRC DCC ファイル/チャット) | tests/ | テスト +// TRANSLATION MISSING +|    fuzz/ | Fuzz testing. +// TRANSLATION MISSING +|       core/ | Fuzz testing for core functions. |    unit/ | 単体テスト |       core/ | コア関数の単体テスト // TRANSLATION MISSING @@ -456,6 +460,18 @@ WeeChat "core" は以下のディレクトリに配置されています: |=== | パス/ファイル名 | 説明 | tests/ | テスト用のルートディレクトリ +// TRANSLATION MISSING +|    fuzz/ | Root of fuzz testing. +// TRANSLATION MISSING +|       core/ | Root of fuzz testing for core. +// TRANSLATION MISSING +|          calc-fuzzer.c | Fuzz testing: calculation of expressions. +// TRANSLATION MISSING +|          crypto-fuzzer.c | Fuzz testing: cryptographic functions. +// TRANSLATION MISSING +|          string-fuzzer.c | Fuzz testing: 文字列 +// TRANSLATION MISSING +|          utf8-fuzzer.c | Fuzz testing: UTF-8. |    unit/ | 単体テスト用のルートディレクトリ |       tests.cpp | 全テストの実行時に使われるプログラム // TRANSLATION MISSING diff --git a/doc/sr/weechat_dev.sr.adoc b/doc/sr/weechat_dev.sr.adoc index 0c1d50455..7b98588f5 100644 --- a/doc/sr/weechat_dev.sr.adoc +++ b/doc/sr/weechat_dev.sr.adoc @@ -91,6 +91,10 @@ qweechat:: |       typing/ | Typing додатак. |       xfer/ | Xfer додатак (IRC DCC фајл/разговор). | tests/ | Тестови. +// TRANSLATION MISSING +|    fuzz/ | Fuzz testing. +// TRANSLATION MISSING +|       core/ | Fuzz testing for core functions. |    unit/ | Unit тестови. |       core/ | Unit тестови за функције језгра. // TRANSLATION MISSING @@ -422,6 +426,18 @@ WeeChat „језгро” се налази у следећим директо |=== | Путања/фајл | Опис | tests/ | Корен тестова. +// TRANSLATION MISSING +|    fuzz/ | Root of fuzz testing. +// TRANSLATION MISSING +|       core/ | Root of fuzz testing for core. +// TRANSLATION MISSING +|          calc-fuzzer.c | Fuzz testing: калкулација израза. +// TRANSLATION MISSING +|          crypto-fuzzer.c | Fuzz testing: криптографске функције. +// TRANSLATION MISSING +|          string-fuzzer.c | Fuzz testing: стрингови. +// TRANSLATION MISSING +|          utf8-fuzzer.c | Fuzz testing: UTF-8. |    unit/ | Корен unit тестова. |       tests.cpp | Програм који се користи за извршавање свих тестова. |       tests-record.cpp | Бележење и претрага у приказаним порукама. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 333a3beb1..8b506853a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,6 +19,19 @@ # along with WeeChat. If not, see . # -if(ENABLE_TESTS) - add_subdirectory(unit) +if(ENABLE_FUZZ) + add_subdirectory(fuzz) +endif() + +if(ENABLE_TESTS) + find_package(CppUTest) + if(CPPUTEST_FOUND) + enable_testing() + add_subdirectory(unit) + else() + message(SEND_ERROR "CppUTest not found") + endif() +else() + enable_testing() + add_test(NAME notests COMMAND true) endif() diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt new file mode 100644 index 000000000..c2728aef8 --- /dev/null +++ b/tests/fuzz/CMakeLists.txt @@ -0,0 +1,101 @@ +# +# SPDX-FileCopyrightText: 2025 Sébastien Helleu +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This file is part of WeeChat, the extensible chat client. +# +# WeeChat is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# WeeChat is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with WeeChat. If not, see . +# + +if(NOT DEFINED ENV{LIB_FUZZING_ENGINE}) + set(ENV{LIB_FUZZING_ENGINE} "-fsanitize=address,fuzzer") +endif() + +remove_definitions(-DHAVE_CONFIG_H) +include_directories( + ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if(NOT CYGWIN) + add_definitions(-fPIC) +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD" AND HAVE_BACKTRACE) + list(APPEND EXTRA_LIBS "execinfo") +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") + list(APPEND EXTRA_LIBS "socket" "nsl") +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Haiku") + list(APPEND EXTRA_LIBS "network") +endif() + +if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Haiku") + list(APPEND EXTRA_LIBS "pthread") +endif() + +if(ICONV_LIBRARY) + list(APPEND EXTRA_LIBS ${ICONV_LIBRARY}) +endif() + +if(LIBINTL_LIBRARY) + list(APPEND EXTRA_LIBS ${LIBINTL_LIBRARY}) +endif() + +list(APPEND EXTRA_LIBS "m") + +list(APPEND EXTRA_LIBS ${ZLIB_LIBRARY}) + +if(ENABLE_ZSTD) + list(APPEND EXTRA_LIBS ${LIBZSTD_LDFLAGS}) +endif() + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # link with resolv lib on macOS + list(APPEND EXTRA_LIBS "resolv") +endif() + +list(APPEND FUZZ_TARGET_LINK + weechat_core + weechat_plugins + weechat_gui_common + weechat_gui_headless + weechat_ncurses_fake + ${EXTRA_LIBS} + ${ZLIB_LIBRARY} + ${LIBZSTD_LDFLAGS} + -rdynamic +) +list(APPEND FUZZ_TARGET_DEPS + weechat_core + weechat_plugins + weechat_gui_common + weechat_gui_headless + weechat_ncurses_fake +) + +# fuzz targets +set(FUZZ_CORE_TARGETS calc crypto string utf8) + +foreach(fuzz_target ${FUZZ_CORE_TARGETS}) + add_executable(weechat_core_${fuzz_target}_fuzzer core/${fuzz_target}-fuzzer.c) + target_link_libraries(weechat_core_${fuzz_target}_fuzzer ${FUZZ_TARGET_LINK}) + set_target_properties(weechat_core_${fuzz_target}_fuzzer PROPERTIES LINK_FLAGS "$ENV{LIB_FUZZING_ENGINE}") + add_dependencies(weechat_core_${fuzz_target}_fuzzer ${FUZZ_TARGET_DEPS}) +endforeach() diff --git a/tests/fuzz/core/calc-fuzzer.c b/tests/fuzz/core/calc-fuzzer.c new file mode 100644 index 000000000..b7e0619a8 --- /dev/null +++ b/tests/fuzz/core/calc-fuzzer.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2025 Sébastien Helleu + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see . + */ + +/* Fuzz testing on WeeChat core calc functions */ + +#include +#include +#include +#include + +#include "src/core/core-calc.h" + +int +LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + char *str; + + str = (char *)malloc (size + 1); + memcpy (str, data, size); + str[size] = '\0'; + + free (calc_expression (str)); + + free (str); + + return 0; +} diff --git a/tests/fuzz/core/crypto-fuzzer.c b/tests/fuzz/core/crypto-fuzzer.c new file mode 100644 index 000000000..00c5aead5 --- /dev/null +++ b/tests/fuzz/core/crypto-fuzzer.c @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Sébastien Helleu + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see . + */ + +/* Fuzz testing on WeeChat core crypto functions */ + +#include +#include +#include +#include +#include + +#include + +#include "src/core/core-crypto.h" + +extern char *weecrypto_hash_algo_string[]; +extern int weecrypto_hash_algo[]; + +int +LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + char *str, hash[1024], *result; + int i, hash_size, key_size, salt_size; + + str = (char *)malloc (size + 1); + memcpy (str, data, size); + str[size] = '\0'; + + key_size = (size > 8) ? 8 : size; + for (i = 0; weecrypto_hash_algo_string[i]; i++) + { + weecrypto_hash (data, size, weecrypto_hash_algo[i], hash, &hash_size); + weecrypto_hmac (data, key_size, data, size, weecrypto_hash_algo[i], hash, &hash_size); + } + + salt_size = (size > 8) ? 8 : size; + weecrypto_hash_pbkdf2 (data, size, GCRY_MD_SHA1, data, salt_size, 100, hash, &hash_size); + weecrypto_hash_pbkdf2 (data, size, GCRY_MD_SHA256, data, salt_size, 100, hash, &hash_size); + weecrypto_hash_pbkdf2 (data, size, GCRY_MD_SHA512, data, salt_size, 100, hash, &hash_size); + + result = weecrypto_totp_generate (str, 1746358623, 6); + if (result && result[0]) + assert (weecrypto_totp_validate (str, 1746358623, 0, result)); + free (result); + result = weecrypto_totp_generate (str, 1746358623, 12); + if (result && result[0]) + assert (weecrypto_totp_validate (str, 1746358623, 0, result)); + free (result); + + free (str); + + return 0; +} diff --git a/tests/fuzz/core/string-fuzzer.c b/tests/fuzz/core/string-fuzzer.c new file mode 100644 index 000000000..19d0522e1 --- /dev/null +++ b/tests/fuzz/core/string-fuzzer.c @@ -0,0 +1,228 @@ +/* + * SPDX-FileCopyrightText: 2025 Sébastien Helleu + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see . + */ + +/* Fuzz testing on WeeChat core string functions */ + +#include +#include +#include +#include +#include +#include + +#include "src/core/core-config.h" +#include "src/core/core-string.h" +#include "src/plugins/weechat-plugin.h" + +regex_t global_regex; + +int +LLVMFuzzerInitialize(int *argc, char ***argv) +{ + (void) argc; + (void) argv; + + config_weechat_init (); + + regcomp (&global_regex, "a.*", 0); + + return 0; +} + +char * +callback_replace (void *data, const char *text) +{ + (void) data; + (void) text; + + return strdup ("z"); +} + +int +LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + char *str, *str2, **str_dyn, *result, *masks[3] = { "a*", "b*", NULL}; + char **argv, *buffer; + const char *name; + int argc, flags, num_tags, priority; + regex_t regex; + int rc; + + str = (char *)malloc (size + 1); + memcpy (str, data, size); + str[size] = '\0'; + + free (string_strndup (str, size / 2)); + + free (string_cut (str, size / 2, 0, 0, "…")); + free (string_cut (str, size / 2, 1, 0, "…")); + free (string_cut (str, size / 2, 0, 1, "…")); + free (string_cut (str, size / 2, 1, 1, "…")); + + free (string_reverse (str)); + + free (string_reverse_screen (str)); + + free (string_repeat (str, 2)); + + free (string_tolower (str)); + + free (string_toupper (str)); + + free (string_tolower_range (str, 13)); + + free (string_toupper_range (str, 13)); + + string_strcmp (str, str); + + string_strncmp (str, str, size / 2); + + string_strcasecmp (str, str); + + string_strcasecmp_range (str, str, 13); + + string_strncasecmp (str, str, size / 2); + + string_strcmp_ignore_chars (str, str, "abcd", 0); + string_strcmp_ignore_chars (str, str, "abcd", 1); + + string_strcasestr (str, str); + + string_match (str, "a*", 0); + string_match (str, "a*", 1); + + string_match_list (str, (const char **)masks, 0); + string_match_list (str, (const char **)masks, 1); + + free (string_expand_home (str)); + + free (string_eval_path_home (str, NULL, NULL, NULL)); + + free (string_remove_quotes (str, "'\"")); + + free (string_strip (str, 0, 0, "abcdef")); + free (string_strip (str, 0, 1, "abcdef")); + free (string_strip (str, 1, 0, "abcdef")); + free (string_strip (str, 1, 1, "abcdef")); + + free (string_convert_escaped_chars (str)); + + string_is_whitespace_char (str); + + string_is_word_char_highlight (str); + + string_is_word_char_input (str); + + free (string_mask_to_regex (str)); + + string_regex_flags (str, 0, NULL); + string_regex_flags (str, 0, &flags); + + rc = string_regcomp (®ex, str, REG_ICASE | REG_NOSUB); + if (rc == 0) + regfree (®ex); + str2 = (char *)malloc (16 + size + 1); + snprintf (str2, 16 + size + 1, "(?ins)%s", str); + rc = string_regcomp (®ex, str2, REG_ICASE | REG_NOSUB); + if (rc == 0) + regfree (®ex); + free (str2); + + string_has_highlight (str, "a"); + + string_has_highlight_regex_compiled (str, &global_regex); + + string_has_highlight_regex (str, "a.*"); + + free (string_replace (str, "a", "b")); + + free (string_replace_regex (str, &global_regex, "b", '$', &callback_replace, NULL)); + + free (string_translate_chars (str, "abc", "def")); + + argv = string_split (str, "/", NULL, 0, 0, &argc); + string_free_split (argv); + argv = string_split (str, "/", " ", 0, 0, &argc); + string_free_split (argv); + flags = WEECHAT_STRING_SPLIT_STRIP_LEFT + | WEECHAT_STRING_SPLIT_STRIP_RIGHT + | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS + | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS; + argv = string_split (str, "/", " ", flags, 0, &argc); + result = string_rebuild_split_string ((const char **)argv, "/", -1, -1); + if (argv && result) + assert (strcmp (str, result) == 0); + free (result); + string_free_split (argv); + + string_free_split_shared (string_split_shared (str, "/", " ", flags, 0, &argc)); + + string_free_split_command (string_split_command (str, ';')); + + string_free_split_tags (string_split_tags (str, &num_tags)); + + free (string_iconv (0, "utf-8", "iso-8859-1", str)); + + free (string_iconv_to_internal ("iso-8859-1", str)); + + free (string_iconv_from_internal ("iso-8859-1", str)); + + free (string_format_size (size)); + + string_parse_size (str); + + buffer = malloc ((size * 4) + 8 + 1); + string_base16_encode (str, size, buffer); + string_base16_decode (str, buffer); + string_base32_encode (str, size, buffer); + string_base32_decode (str, buffer); + string_base64_encode (0, str, size, buffer); + string_base64_encode (1, str, size, buffer); + string_base64_decode (0, str, buffer); + string_base64_decode (1, str, buffer); + free (buffer); + + free (string_hex_dump (str, size, 16, "<", ">")); + + string_is_command_char (str); + + string_input_for_buffer (str); + + string_get_common_bytes_count (str, str); + + string_levenshtein (str, str, 0); + string_levenshtein (str, str, 1); + + string_get_priority_and_name (str, &priority, &name, 0); + + string_shared_free ((char *)string_shared_get (str)); + + str_dyn = string_dyn_alloc (1); + string_dyn_copy (str_dyn, str); + string_dyn_concat (str_dyn, str, -1); + string_dyn_free (str_dyn, 1); + + string_concat ("/", str, str, NULL); + + free (str); + + return 0; +} diff --git a/tests/fuzz/core/utf8-fuzzer.c b/tests/fuzz/core/utf8-fuzzer.c new file mode 100644 index 000000000..9d8bac23e --- /dev/null +++ b/tests/fuzz/core/utf8-fuzzer.c @@ -0,0 +1,103 @@ +/* + * SPDX-FileCopyrightText: 2025 Sébastien Helleu + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see . + */ + +/* Fuzz testing on WeeChat core UTF-8 functions */ + +#include +#include +#include +#include + +#include "src/core/core-config.h" +#include "src/core/core-utf8.h" + +int +LLVMFuzzerInitialize(int *argc, char ***argv) +{ + (void) argc; + (void) argv; + + config_weechat_init (); + + return 0; +} + +int +LLVMFuzzerTestOneInput (const uint8_t *data, size_t size) +{ + char *str, *str2, utf8_char[5], *error; + size_t i; + + str = (char *)malloc (size + 1); + memcpy (str, data, size); + str[size] = '\0'; + + utf8_has_8bits (str); + + utf8_is_valid (str, size, &error); + + str2 = strdup (str); + utf8_normalize (str2, '?'); + free (str2); + + for (i = 0; i < 5; i++) + { + if (size >= i) + { + utf8_prev_char (str, str + i); + utf8_beginning_of_line (str, str + i); + } + } + + utf8_next_char (str); + + utf8_end_of_line (str); + + utf8_char_int (str); + + utf8_int_string (utf8_char_int (str), utf8_char); + + utf8_char_size (str); + + utf8_strlen (str); + + utf8_strnlen (str, size / 2); + + utf8_char_size_screen (str); + + utf8_strlen_screen (str); + + if (size > 4) + { + utf8_add_offset (str, 1); + utf8_real_pos (str, 1); + utf8_pos (str, 1); + } + + free (utf8_strndup (str, size / 2)); + + if (size > 4) + utf8_strncpy (utf8_char, str, 1); + + free (str); + + return 0; +} diff --git a/tests/fuzz/weechat_core_calc_fuzzer.dict b/tests/fuzz/weechat_core_calc_fuzzer.dict new file mode 100644 index 000000000..5bc3edb81 --- /dev/null +++ b/tests/fuzz/weechat_core_calc_fuzzer.dict @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2025 Sébastien Helleu +# +# SPDX-License-Identifier: GPL-3.0-or-later + +"0" +"1" +"2" +"3" +"4" +"5" +"6" +"7" +"8" +"9" +"." +"(" +")" +"+" +"-" +"*" +"/" +"//" +"%" +"**"