Skip to content

fuzz: add fuzz_qos_provider target for the sysdef XML parser - #2442

Open
glaziermag wants to merge 1 commit into
eclipse-cyclonedds:masterfrom
glaziermag:pr/fuzz-qos-provider
Open

fuzz: add fuzz_qos_provider target for the sysdef XML parser#2442
glaziermag wants to merge 1 commit into
eclipse-cyclonedds:masterfrom
glaziermag:pr/fuzz-qos-provider

Conversation

@glaziermag

@glaziermag glaziermag commented Aug 5, 2026

Copy link
Copy Markdown

Adds a libFuzzer target for the system-definition XML parser, driven through the public QoS provider
API.

src/core/ddsc/src/dds_sysdef_parser.c and dds_qos_provider.c currently have no fuzz coverage —
none of the seven existing targets reach dds_sysdef_init_sysdef_str(). (The shared ddsrt_xmlp_*
tokenizer in src/ddsrt/src/xmlparser.c is already exercised, via fuzz_config_init on the DDSI
config path; what is uncovered is the sysdef callback layer above it and the QoS provider.)

Design notes

Inline XML only. read_sysdef — reached from dds_create_qos_provider — treats an argument
starting with < as an inline document and anything else as a filesystem path
(dds_qos_provider.c:29-40). The target rejects anything not starting with <, so fuzzer bytes
never reach the fopen branch; otherwise the target would depend on the filesystem and stop being
reproducible.

Guarded on ENABLE_QOS_PROVIDER in fuzz/CMakeLists.txt. This is load-bearing rather than
cosmetic: with the option OFF, src/core/ddsc/CMakeLists.txt drops dds_qos_provider.c from the
sources and the public header from the install set, so the target would fail to compile and link.
add_subdirectory(src) runs before add_subdirectory(fuzz), so the cache entry is visible.

Keyed lookup uses literal names. dds_qos_provider_get_qos matches keys with strcmp against
"<library>::<profile>", so the target queries "L::P" and "OurLibrary::ProfileA", which are the
names in the seed corpus. A wildcard would never resolve.

No change needed to fuzz/oss-fuzz-build.sh. It already finds build/bin/fuzz_*, copies
fuzz/*.options, and zips fuzz_*_seed_corpus/ directories recursively, so the target, its
.options file and its corpus are picked up as-is.

Seed corpus derives from src/core/ddsc/tests/sysdef_qos_library.xml plus documents exercising
the base64 user_data/topic_data/group_data policies, a reader-history profile, and <dds/>.

Verification

  • infra/helper.py build_fuzzers cyclonedds <tree> and check_build cyclonedds — both exit 0;
    fuzz_qos_provider built alongside the seven existing targets, with its .options and
    seed_corpus.zip in $OUT. This is the default engine and sanitizer only (libfuzzer + address);
    I have not built the afl, honggfuzz or UBSan configurations that project.yaml also lists.
  • Separately, a locally hand-linked ASan+libFuzzer build for iteration. The five seeds reach 622 of
    the binary's ~35,000 instrumented edges; 20 minutes of fuzzing on top only reached 831, so most of
    what the target finds early comes from the seeds rather than from mutation.

It reproduces two open issues immediately

Flagging this so it isn't a surprise after merge. The target finds both within seconds:

(#2440 is also triggered by an empty file, which this target can't submit since it requires a
leading <; the element-free document is the form it finds.)

Both issues carry a suggested fix. Sequencing is yours — landing fixes first and this second is a
perfectly sensible order.

What the local run does and doesn't show

A 20-minute run (macOS/arm64, ASan, -fork=2) produced ~4,600 crash artifacts. I triaged a
400-artifact sample: 364 were the #2440 SEGV, 36 the #2441 over-read, nothing else. I did not triage
the remainder, and I have not run this on Linux/x86_64 or under UBSan — so please read that as a
sample rather than a clean bill of health.

The system-definition parser (dds_sysdef_parser.c) and the QoS provider
that drives it have no fuzz coverage: none of the seven existing targets
reach dds_sysdef_init_sysdef_str().

Drives the public dds_create_qos_provider() API, then the keyed
dds_qos_provider_get_qos() lookup using literal library::profile names
from the seed corpus, since that lookup matches keys with strcmp.

Only the inline-XML form is fuzzed. dds_create_qos_provider() treats an
argument not starting with '<' as a filesystem path, and sending fuzzer
bytes down that branch would make the target depend on the filesystem.

Guarded on ENABLE_QOS_PROVIDER, which drops dds_qos_provider.c from the
build when OFF. No change to fuzz/oss-fuzz-build.sh is needed; it already
finds build/bin/fuzz_*, copies fuzz/*.options and zips seed corpora.

Signed-off-by: glaziermag <leuping@gmail.com>
@glaziermag
glaziermag force-pushed the pr/fuzz-qos-provider branch from 18b9c7a to 4211458 Compare August 6, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant