diff --git a/lib/common/fuzzers/acl_fuzzer.c b/lib/common/fuzzers/acl_fuzzer.c new file mode 100644 index 00000000000..a81264bd385 --- /dev/null +++ b/lib/common/fuzzers/acl_fuzzer.c @@ -0,0 +1,55 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include +#include + +#include +#include +#include + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *input = NULL; + xmlNode *xml = NULL; + xmlNode *result = NULL; + + if (size < 20) { + return -1; + } + + // Null-terminate the fuzz input + input = pcmk__assert_alloc(size + 1, sizeof(char)); + memcpy(input, data, size); + input[size] = '\0'; + + // Parse the fuzz input as XML + xml = pcmk__xml_parse(input); + if (xml == NULL) { + free(input); + return 0; + } + + // Run the ACL filtered copy with a non-root user + // pcmk_acl_required() returns false for "root" and "hacluster", so we use + // a regular user name to ensure ACL processing is actually exercised. + xml_acl_filtered_copy("fuzzuser", xml, xml, &result); + + if (result != NULL) { + pcmk__xml_free(result); + } + + pcmk__xml_free(xml); + free(input); + return 0; +} diff --git a/lib/common/fuzzers/patchset_fuzzer.c b/lib/common/fuzzers/patchset_fuzzer.c new file mode 100644 index 00000000000..0998b071e72 --- /dev/null +++ b/lib/common/fuzzers/patchset_fuzzer.c @@ -0,0 +1,82 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include +#include + +#include +#include +#include + +/* A minimal but realistic CIB structure that the patchset will be applied to. + * This provides enough structure for XPath operations to have meaningful + * targets (nodes, resources, constraints, status). + */ +static const char *BASE_CIB = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *input = NULL; + xmlNode *patchset = NULL; + xmlNode *cib = NULL; + + if (size < 15) { + return -1; + } + + // Parse a fresh copy of the base CIB for each iteration + cib = pcmk__xml_parse(BASE_CIB); + if (cib == NULL) { + return 0; + } + + // Parse the fuzz input as a patchset + input = pcmk__assert_alloc(size + 1, sizeof(char)); + memcpy(input, data, size); + input[size] = '\0'; + + patchset = pcmk__xml_parse(input); + if (patchset == NULL) { + pcmk__xml_free(cib); + free(input); + return 0; + } + + // Apply the fuzz-generated patchset to the base CIB + // Disable version checking to maximize code path exploration + xml_apply_patchset(cib, patchset, false); + + pcmk__xml_free(patchset); + pcmk__xml_free(cib); + free(input); + return 0; +} diff --git a/lib/common/fuzzers/rules_fuzzer.c b/lib/common/fuzzers/rules_fuzzer.c new file mode 100644 index 00000000000..e908bff138a --- /dev/null +++ b/lib/common/fuzzers/rules_fuzzer.c @@ -0,0 +1,56 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *input = NULL; + xmlNode *xml = NULL; + crm_time_t *now = NULL; + pcmk_rule_input_t rule_input = { NULL, }; + + if (size < 10) { + return -1; + } + + // Null-terminate the fuzz input + input = pcmk__assert_alloc(size + 1, sizeof(char)); + memcpy(input, data, size); + input[size] = '\0'; + + // Parse the fuzz input as XML — rules are always XML-based + xml = pcmk__xml_parse(input); + if (xml == NULL) { + free(input); + return 0; + } + + // Set up a minimal rule evaluation context with a fixed "now" time + now = pcmk__copy_timet(1700000000); // Fixed time for determinism + rule_input.now = now; + + // Evaluate the parsed XML as a rule + pcmk_evaluate_rule(xml, &rule_input, NULL); + + crm_time_free(now); + pcmk__xml_free(xml); + free(input); + return 0; +} diff --git a/lib/common/fuzzers/xml_parse_fuzzer.c b/lib/common/fuzzers/xml_parse_fuzzer.c new file mode 100644 index 00000000000..a385eee5c3b --- /dev/null +++ b/lib/common/fuzzers/xml_parse_fuzzer.c @@ -0,0 +1,55 @@ +/* + * Copyright 2026 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU Lesser General Public License + * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include +#include + +#include +#include + +int +LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + char *input = NULL; + xmlNode *xml = NULL; + + if (size < 5) { + return -1; + } + + // Null-terminate the input to create a valid C string + input = pcmk__assert_alloc(size + 1, sizeof(char)); + memcpy(input, data, size); + input[size] = '\0'; + + // Parse the XML string — this is the core function under test + xml = pcmk__xml_parse(input); + + // If parsing succeeded, exercise some read-only operations on the result + if (xml != NULL) { + // Access the element name and ID (common post-parse operations) + pcmk__xe_id(xml); + + // Iterate children — exercises XML tree traversal + for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL); + child != NULL; + child = pcmk__xe_next(child, NULL)) { + + pcmk__xe_id(child); + } + + pcmk__xml_free(xml); + } + + free(input); + return 0; +}