From b6974030e6a7ddb330894f46631c8da4359b2d18 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Mon, 13 May 2024 10:39:44 +1200 Subject: [PATCH] lib/fuzzing: add fuzz_strncasecmp_ldb As well as checking for the usual overflows, this asserts that strncasecmp_ldb is always transitive, by splitting the input into 3 pieces and comparing all pairs. Signed-off-by: Douglas Bagnall Reviewed-by: Andrew Bartlett --- lib/fuzzing/fuzz_strncasecmp_ldb.c | 161 +++++++++++++++++++++++++++++ lib/fuzzing/wscript_build | 5 + 2 files changed, 166 insertions(+) create mode 100644 lib/fuzzing/fuzz_strncasecmp_ldb.c diff --git a/lib/fuzzing/fuzz_strncasecmp_ldb.c b/lib/fuzzing/fuzz_strncasecmp_ldb.c new file mode 100644 index 00000000000..0f785b5bee7 --- /dev/null +++ b/lib/fuzzing/fuzz_strncasecmp_ldb.c @@ -0,0 +1,161 @@ +/* + Fuzzing ldb_comparison_fold() + Copyright (C) Catalyst IT 2020 + + This program 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. + + This program 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 this program. If not, see . +*/ +#include "includes.h" +#include "fuzzing/fuzzing.h" +#include "charset.h" + + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + return 0; +} + + +int LLVMFuzzerTestOneInput(const uint8_t *input, size_t len) +{ + struct ldb_val v[3] = {{},{},{}}; + size_t i, j, k; + int results[9], ab, ac, bc; + + if (len < 3) { + return 0; + } + + j = 0; + k = 0; + v[j].data = discard_const(input); + + /* + * We split the input into 3 ldb_vals, on the byte '*' (42), chosen + * because it is *not* special with regard to termination, utf-8, or + * casefolding. + * + * if there are not 2 '*' bytes, the last value[s] will be empty, with + * a NULL pointer and zero length. + */ + + for (i = 0; i < len; i++) { + if (input[i] != '*') { + continue; + } + v[j].length = i - k; + i++; + j++; + if (j > 2 || i == len) { + break; + } + k = i; + v[j].data = discard_const(input + k); + } + + for (i = 0; i < 3; i++) { + char *s1 = (char*)v[i].data; + size_t len1 = v[i].length; + for (j = 0; j < 3; j++) { + char *s2 = (char*)v[j].data; + size_t len2 = v[j].length; + int r = strncasecmp_ldb(s1, len1, s2, len2); + if (abs(r) > 1) { + abort(); + } + results[i * 3 + j] = r; + } + } + + /* + * There are nine comparisons we make. + * + * A B C + * A = x x + * B - = x + * C - - = + * + * The diagonal should be all zeros (A == A, etc) + * The upper and lower triangles should complement each other + * (A > B implies B < A; A == B implies B == A). + * + * So we check for those identities first. + */ + + if ((results[0] != 0) || + (results[4] != 0) || + (results[8] != 0)) { + abort(); + } + + ab = results[3]; + ac = results[6]; + bc = results[7]; + + if (ab != -results[1] || + ac != -results[2] || + bc != -results[5]) { + abort(); + } + + /* + * Then there are 27 states within the three comparisons of one + * triangle, because each of AB, AC, and BC can be in 3 states. + * + * 0 (A < B) (A < C) (B < C) A < B < C + * 1 (A < B) (A < C) (B = C) A < (B|C) + * 2 (A < B) (A < C) (B > C) A < C < B + * 3 (A < B) (A = C) (B < C) invalid + * 4 (A < B) (A = C) (B = C) invalid + * 5 (A < B) (A = C) (B > C) (A|C) < B + * 6 (A < B) (A > C) (B < C) invalid + * 7 (A < B) (A > C) (B = C) invalid + * 8 (A < B) (A > C) (B > C) C < A < B + * 9 (A = B) (A < C) (B < C) (A|B) < C + * 10 (A = B) (A < C) (B = C) invalid + * 11 (A = B) (A < C) (B > C) invalid + * 12 (A = B) (A = C) (B < C) invalid + * 13 (A = B) (A = C) (B = C) A = B = C + * 14 (A = B) (A = C) (B > C) invalid + * 15 (A = B) (A > C) (B < C) invalid + * 16 (A = B) (A > C) (B = C) invalid + * 17 (A = B) (A > C) (B > C) C < (A|B) + * 18 (A > B) (A < C) (B < C) B < C < A + * 19 (A > B) (A < C) (B = C) invalid + * 20 (A > B) (A < C) (B > C) invalid + * 21 (A > B) (A = C) (B < C) B < (A|C) + * 22 (A > B) (A = C) (B = C) invalid + * 23 (A > B) (A = C) (B > C) invalid + * 24 (A > B) (A > C) (B < C) B < C < A + * 25 (A > B) (A > C) (B = C) (B|C) < A + * 26 (A > B) (A > C) (B > C) C < B < A + * + * It actually turns out to be quite simple: + */ + + if (ab == 0) { + if (ac != bc) { + abort(); + } + } else if (ab < 0) { + if (ac >= 0 && bc <= 0) { + abort(); + } + } else { + if (ac <= 0 && bc >= 0) { + abort(); + } + } + + return 0; +} diff --git a/lib/fuzzing/wscript_build b/lib/fuzzing/wscript_build index 897a114ca7e..ce2684580ce 100644 --- a/lib/fuzzing/wscript_build +++ b/lib/fuzzing/wscript_build @@ -169,6 +169,11 @@ bld.SAMBA_BINARY('fuzz_security_token_vs_descriptor_ds', deps='fuzzing samba-security afl-fuzz-main', fuzzer=True) +bld.SAMBA_BINARY('fuzz_strncasecmp_ldb', + source='fuzz_strncasecmp_ldb.c', + deps='fuzzing samba-util afl-fuzz-main', + fuzzer=True) + # The fuzz_type and fuzz_function parameters make the built # fuzzer take the same input as ndrdump and so the same that -- 2.47.3