-
Notifications
You must be signed in to change notification settings - Fork 355
add four new fuzzing harnesses #4076
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <crm_internal.h> | ||
|
|
||
| #include <stdint.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| #include <crm/common/util.h> | ||
| #include <crm/common/internal.h> | ||
| #include <crm/common/acl.h> | ||
|
|
||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you want, you can do all the freeing after a It doesn't matter to me. Note that this applies to other files in this commit too. |
||
| 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); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be worth fuzzing with different usernames (returning Speaking of which... I don't know whether or how the corpus gets seeded with valid inputs. If we're relying on purely random data here, I would expect it to take an unreasonably long time to get anything remotely meaningful, even if we get valid XML. If all we care about is testing garbage input, that's fine. |
||
|
|
||
| if (result != NULL) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This doesn't need to be guarded. |
||
| pcmk__xml_free(result); | ||
| } | ||
|
|
||
| pcmk__xml_free(xml); | ||
| free(input); | ||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <crm_internal.h> | ||
|
|
||
| #include <stdint.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| #include <crm/common/util.h> | ||
| #include <crm/common/internal.h> | ||
| #include <crm/common/xml.h> | ||
|
|
||
| /* 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 = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We usually reserve all-caps for |
||
| "<cib admin_epoch=\"1\" epoch=\"1\" num_updates=\"0\">" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm pretty sure you can use single quotes within the XML and avoid all the backslash-escaping |
||
| " <configuration>" | ||
| " <crm_config>" | ||
| " <cluster_property_set id=\"cib-bootstrap-options\">" | ||
| " <nvpair id=\"opt1\" name=\"stonith-enabled\" value=\"false\"/>" | ||
| " </cluster_property_set>" | ||
| " </crm_config>" | ||
| " <nodes>" | ||
| " <node id=\"node1\" uname=\"pcmk-1\"/>" | ||
| " <node id=\"node2\" uname=\"pcmk-2\"/>" | ||
| " </nodes>" | ||
| " <resources>" | ||
| " <primitive id=\"rsc1\" class=\"ocf\" provider=\"heartbeat\"" | ||
| " type=\"Dummy\"/>" | ||
| " </resources>" | ||
| " <constraints/>" | ||
| " </configuration>" | ||
| " <status/>" | ||
| "</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; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 <crm_internal.h> | ||
|
|
||
| #include <stdint.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <time.h> | ||
|
|
||
| #include <crm/common/util.h> | ||
| #include <crm/common/internal.h> | ||
| #include <crm/common/rules.h> | ||
|
|
||
| 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; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /* | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should either change the name of this file or change our documentation.
There is no |
||
| * 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 <crm_internal.h> | ||
|
|
||
| #include <stdint.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
|
|
||
| #include <crm/common/util.h> | ||
| #include <crm/common/internal.h> | ||
|
|
||
| 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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might as well be consistent with the other three files in this commit, and return early if |
||
| // Access the element name and ID (common post-parse operations) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where are we accessing the element name? |
||
| pcmk__xe_id(xml); | ||
|
|
||
| // Iterate children — exercises XML tree traversal | ||
| for (xmlNode *child = pcmk__xe_first_child(xml, NULL, NULL, NULL); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can use |
||
| child != NULL; | ||
| child = pcmk__xe_next(child, NULL)) { | ||
|
|
||
| pcmk__xe_id(child); | ||
| } | ||
|
|
||
| pcmk__xml_free(xml); | ||
| } | ||
|
|
||
| free(input); | ||
| return 0; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get comments explaining the magic size numbers, here and in the other harnesses?