mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-17 05:11:13 +08:00
master
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8e96408b2a |
Fuzz oracles: confirm net48 allocation findings by re-running the target (#4818)
## Problem `FuzzCrashAssets(AflfuzzBinaryDecoder)` failed intermittently on net48 (run 38897795750, test-windows-net48 1/16, on #4815, which changes only `perf/`): > 10562496 bytes allocated for 42 input bytes, budget 8391296 bytes On .NET Framework, `FuzzOracles` measures allocations with `AppDomain.MonitoringTotalAllocatedMemorySize`. That counter covers the whole AppDomain, so it also counts other threads in the test host. Measured on net48 Release for `crash-0bdcbfafc13981ae6f665ad52cabc1cbe21b0238`: | Scenario | Bytes | |---|---| | First decode in the process (one-time XML setup) | 1,083,232 | | Later decodes, same thread or a fresh thread | 16,440 | | Same decode with one concurrent thread allocating | 10.7 to 23.8 MB (over budget) | The decoder does not have a per-thread cost, and #4812 did not regress it. The CI TRX shows that tests ran one at a time. The failure came about 2 s into the run, right after a burst of about 100 fast test cases, which fits vstest serializing results on a background thread. ## Fix `FuzzOracles.RunTarget` re-runs a target when the allocation finding comes from a counter that is not per thread (`AllocationConfirmationRuns = 2`). It reports the finding only if every run confirms it. A real over-budget decode allocates the same amount every time, while a burst on another thread rarely lines up with all three runs. This is the same approach the time oracle already uses (`TimeConfirmationRuns`). Runtimes with a per-thread counter (net8+) behave as before. `FuzzMethods` playback goes through `RunTarget` too, so it also gets the confirmation runs. ## Tests - New tests in `FuzzOraclesTests`: - A finding that every run confirms is still reported, after 3 runs on net48 and 1 run on net8+. - A finding inflated by another thread is ignored (net48 only). This test fails when the confirmation runs are set to 0. - net48 Release: Opc.Ua.Fuzzing.Tests 156/156, Opc.Ua.Encoders.Fuzz.Tests 8130/8130. - net10.0: FuzzOraclesTests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
9b034f8291 |
Enable nullable reference types centrally; migrate all unset and annotations-only projects (#4700)
# Description Nullable reference types are now enabled once, in `common.props`, and every project in the repository builds warning-clean with them. **Configuration** - `<Nullable>enable</Nullable>` lives in `common.props`. All per-project `<Nullable>` settings are removed: 178 project files (enable, annotations, and the `disable` in the three Aot test projects). - 419 per-file `#nullable enable`/`restore` directives and the 4 `#nullable disable` directives are removed. - Kept on purpose: - The 5 `<auto-generated>` schema files (`UANodeSet.cs`, `UA Type Dictionary.cs`, `OPCBinarySchema.cs`, `SecuredApplication.cs`, `PInvokeHelper.cs`) keep `#nullable enable`. The compiler ignores the project setting in generated code, so the directive is required there. - `tests/Opc.Ua.Interop.LegacyPeer` keeps `disable`. It builds against the 1.5.378 packages and does not import the repository props. - `docs/DeveloperGuide.md` and `.github/copilot-instructions.md` now describe the central setting. **Migration** (the 43 unset and 13 annotations-only projects, roughly 10k diagnostics) - **Production code: annotated at the declarations.** Covers `tools/Opc.Ua.SourceGeneration.Core`, `src/Opc.Ua.Lds.Server` and the LDS sample. - Members, parameters and returns are nullable where null actually flows. - Null-check helpers carry `[NotNullWhen]` (`IsNull`, `IsValidXmlName`, `IsMethodTypeDesign`). - `CoreUtils.Format`, `Utils.Format`, `ITemplateWriter.WriteLine` take `params object?[]`. `Template.AddReplacement` takes `string?` (rendering already skips null replacements). - `IRegisteredServerStore.RegisterAsync` returns `Task<RegistrationEntry?>`: it returns null when a registration is removed. - Dereferences backed by an invariant (for example, the validated model) use `!`, so runtime behaviour is unchanged. - **Behavioural changes in production code** (small, deliberate): - `NodeSetToModelDesign` throws `InvalidOperationException` when the file is not a NodeSet. Before, it threw a NullReferenceException on the next line. - `ModelDesignValidator` starts its lookup tables empty instead of null. A redundant `if (m_nodes == null) return;` guard is gone. - **Test code: minimal annotations.** - `?` on values that can be null, `!` where the test asserts non-null. - `= null!` for fixture members assigned in SetUp. - `TryGetStructure<T>(out T? x)` where the inferred `T?` broke the `IEncodeable` constraint. - A few redundant `== null` checks on structs (`NodeId`, `ArrayOf<T>`) folded into the adjacent `IsNull`/`Count` checks. **Notes for reviewers** - The diff is large (~1,500 files) but almost entirely mechanical: `?`, `!` and directive removal. The non-annotation changes are listed above. - Known follow-up, not done here: `IModelDesign.Namespaces/TargetNamespace/NamespaceUris` stay non-nullable even though the validator returns null before it loads a dictionary. Making them nullable would ripple through every generator call site. ## Related Issues - Fixes #4439 ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [ ] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. The "no new warnings" box covers net10.0 and net48: full non-incremental builds of `UA.slnx`, `tools/SourceGeneration.slnx`, `tools/MigrationAnalyzer.slnx` and the Aot test projects finished with 0 warnings and 0 errors under the repository's warnings-as-errors settings. net9.0 and net8.0 also had no nullable diagnostics, but they were not rebuilt after the final fixes. Tests have not been run locally yet. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
52f8520baa |
Detect resource and limit abuse in the encoder/decoder fuzz harness (#4666)
## Why The encoder/decoder fuzz targets only flagged crashes, unhandled exceptions and encoding-fidelity mismatches. They could not observe a decode that stays within the exception contract but consumes far more memory, CPU or stack than its input justifies, or that materialises values larger than the context's encoding limits. None of the resource-related findings of the codec audit would have been caught. ## What Adds four shared oracles to the fuzz harness (`fuzzing/Common/Fuzz/FuzzOracles*.cs`), applied by every host (libFuzzer, AFL, `--replay`, Tools playback and the NUnit replay): - **Allocation** — wraps the Binary/JSON/XML decode cores, the built-in-type targets, the PubSub UADP/JSON decoders and the UADP reassembler; fails when a decode allocates more than `64x` its input plus `8 MB`, whether it returns or throws. Uses the per-thread allocation counter (the AppDomain counter on .NET Framework). - **Time** — a per-input budget of `1 s + 100 ms/KB`, re-run twice and reported only if the fastest run is still over, so JIT/GC/CI noise does not trip it (`OPCUA_FUZZ_TIME_BUDGET_SCALE`, `0` disables). - **Stack** — every input runs on a worker thread with a `256 KB` stack, so recursion that comes close to the limit of a normal thread is caught in-process as a crash rather than near 200 levels. `StackTestcases` inputs replay through every target in a child process (`--fuzz-replay-all`), since such a crash cannot be caught. - **Decoded limits** — walks a decoded value for strings, identifiers, ByteStrings and arrays beyond the context's `MaxStringLength`/`MaxByteStringLength`/`MaxArrayLength`; matrices are additionally boxed the legacy way so a value no consumer can materialise is reported. The fuzz context uses `4096/8192/4096` so small inputs reach the limits. Supporting changes: - `ArrayOf<T>`/`MatrixOf<T>` are not `IEnumerable`, so the limit walker unpacks them via `ToArray`/`ToArrayOf` (otherwise it silently traversed nothing). - `Decimal` is registered in the fuzz context so the targets exercise `Decimal.Decode`. - **B1-2 bug fix**: `BinaryDecoder.ReadExtensionObject` dereferenced a null element for an XML body that is not an element; now rejected with `BadDecodingError`. Found by replaying the nightly crash corpus under the new exception contract; 5 corpus inputs are added as gating reproducers. - Reproducers for B1-1, T1-3, X1-1 and U1-3, built in process by documented builders (`ResourceReproducerTests`, `PubSubReproducerTests`) and checked in to the corpus, where every target replays them under the oracles. Each was verified by reverting the matching fix locally and confirming the guard fires (e.g. B1-1 then allocates ~21 MB from 481 input bytes). - `Fuzzing.md` / `CrashCorpus.md` document the oracles and the private-input log policy. ## Testing Green on net10.0 and net48: | Suite | net10.0 | net48 | |---|---|---| | `Opc.Ua.Encoders.Fuzz.Tests` | 8130 | 8130 | | `Opc.Ua.PubSub.Fuzz.Tests` | 149 | n/a (modern-only) | | `Opc.Ua.Fuzzing.Tests` | 154 | 154 | | `Opc.Ua.Types.Tests` (encoders) | 3698 | builds | Also verified: `Network`/`Certificates` fuzz test suites, all fuzz hosts and Tools projects build, the fuzz manifest validator passes, `dotnet format whitespace` is clean, and the full ~22k-input nightly crash corpus replays with no oracle false positives. Stacked on `romanett/encoder-decoder-spec-security-83c9f5`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
61edaa1ed0 |
Remove netstandard2.1 and net472 target frameworks (#4624)
# Description Drops support for **netstandard2.1** and **.NET Framework 4.7.2**. Libraries now target `net48`, `net8.0`, `net9.0` and `net10.0`. `Opc.Ua.Types` and `Opc.Ua.SourceGeneration.Core` still also target `netstandard2.0`, but only because the Roslyn source generators load them. There is no general netstandard2.0 support. **Preprocessor directives** (112 files) - Every `#if`/`#elif` mentioning `NETSTANDARD2_1*`, `NET472*`, `NET_STANDARD_TESTS`, `SKIP_ECC_CERTIFICATE_REQUEST_SIGNING` or `NETSTANDARD*` was evaluated against the remaining TFM set (net48, net8/9/10, plus netstandard2.0 inside `Opc.Ua.Types`/`tools`) and simplified. Dead branches were removed. - Example: `NETSTANDARD2_1 || NET8_0_OR_GREATER` → `NET8_0_OR_GREATER`. - Example: `NET8_0_OR_GREATER || NET472_OR_GREATER` → removed (always true). - Netstandard-only code was removed: `UnixFileModeShim`, the CRDT reflection path in `ReplicatedGossipOptions.CreateReaderOptions`, and the netstandard2.1-only test variants. **Custom test logic** - `targets.props` drops the `net472`, `netstandard2.0` and `netstandard2.1` `CustomTestTarget` profiles, `NetStandardTests` and `SKIP_ECC_CERTIFICATE_REQUEST_SIGNING`. Supported values are now `net48`, `net8.0`, `net9.0`, `net10.0`. - The `NET_STANDARD_TESTS` define is removed from 29 test projects. - The "build netstandard shells as net10.0" overrides are removed from 18 projects. - `RestrictForLegacyTfm` now only applies to `net48`. - `tests/customtest.bat` is updated to the new values. **Build / packaging / CI** - MSBuild TFM conditions are simplified: Kestrel package choice, MQTTnet v4 (net48 only), BouncyCastle, Di/OpenUsd/Vision/Stress test excludes, and `System.IO.FileSystem.AccessControl` (dropped from Core). - The MigrationAnalyzer nuspec drops its net472/netstandard2.1 lib groups. The Azure signing lists are updated. - `get-ci-matrix.ps1` removes the net472/netstandard test profiles (including the PR-gating `linux-netstandard2.1`) and the matching build legs. **Breaking API change (2.0 preview):** removed `HttpsTransportListener.IsWssTransportSupported` and `ReplicatedGossipOptions.IsGossipStateDecodingSupported`. They only returned `false` on netstandard2.1 and are now always `true`. The test guards that used them were removed as well. **Docs** - NuGet READMEs, `README.md`, `DeveloperGuide.md`, `PubSub.md`, the certificate/ECC/identity docs and the 2.0 migration guide (plus its `.agents` skill mirror) are updated. - The migration guide notes that 4.7.2 consumers must move to 4.8 and netstandard2.1 consumers to net8.0+. ### Reviewer notes - The `System.IO.FileSystem.AccessControl` `PackageVersion` pin is left in `Directory.Packages.props`. It has no direct reference any more, but transitive pinning is on, so it was kept to avoid shifting resolved versions. - The `#if` rewrite was done mechanically by a truth-table evaluator. The diff in the `.cs` files is limited to directive lines, removed dead branches, and stale-comment updates. ### Verified locally - `dotnet build UA.slnx -p:CustomTestTarget=net48`: 0 errors. - `dotnet build UA.slnx -p:CustomTestTarget=net10.0`: 0 errors. - Default multi-target builds (no `CustomTestTarget`) of Opc.Ua.Types, Bindings.Https, PubSub.Mqtt and Redundancy.Server succeeded. - net10.0 tests: - Opc.Ua.Redundancy.Server.Tests: 840/840 passed. - Opc.Ua.Types.Tests: 11363 passed, 1 skipped. - ModelDependencyScanner tests: 20/20 passed. - The full test suite was **not** run locally. ## Related Issues - None ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [ ] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [ ] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
5d26f6f439 |
Fix JSON encoding fidelity issues found by fuzz crash replay and align with Part 6 (#4540)
# Description
The external crash corpus replayed by `Opc.Ua.Encoders.Fuzz.Tests` still
produced several hundred tolerated *encoding fidelity findings* per run.
They came from the 10 BinaryJson conversion targets (binary decode →
JSON encode → JSON decode). None of them was a crash or an unexpected
exception. Triage reduced them to three JSON encoder defects plus one
oracle rule that was stricter than the specification. While checking the
fixes against OPC 10000-6, two adjacent deviations turned up and are
fixed as well.
### Encoder fixes (`Opc.Ua.Types`)
| Defect | Before | After | Spec |
|---|---|---|---|
| LocalizedText with no Text and an empty/null Locale | `{"Text":null}`
(Verbose) / `{}` (Compact). Reads back as null and re-encodes
differently | JSON `null` (Verbose) / omitted (Compact) | 5.4.2.15 Table
39, 5.1.2 Table 1, 5.4.6 Table 45 |
| LocalizedText with empty Text | `"Text":""` written | Text not
encoded; `LocalizedText("")` is the null LocalizedText | 5.4.2.15 Table
39 ("not encoded if it is null or empty") |
| Variant/DataValue holding a **null array**, Compact | `Value` omitted
→ decoded as a *scalar* default (array rank lost) | `"Value":[]` |
5.4.2.17 Table 41, 5.1.11 |
| Variant/DataValue `Value` omission rule | Omitted for any default
value (including null *arrays*) when `IgnoreDefaultValues`;
`IgnoreNullValues` ignored | Same rules as every other field: a NULL of
a nullable built-in type is omitted when `IgnoreNullValues` or
`IgnoreDefaultValues` is set (like `WriteNull`); a default of a
non-nullable type only when `IgnoreDefaultValues` is set (like
`WriteValueProperty`). An array `Value` is never omitted | 5.4.2.17
Table 41, 5.1.2 Table 1, 5.4.6 Table 45 |
| `ExpandedNodeId.Format(context)` with a ServerIndex/NamespaceUri and a
null identifier | Returned `null`, so JSON wrote `null` and the server
index was lost | `svr=N;i=0` / `nsu=...;i=0`, consistent with the
`IFormatProvider` overload | 5.1.12 Table 6, 5.4.2.11, 5.1.2 Table 1
(NULL is exactly `i=0`) |
### Fuzz oracle and harness (`fuzzing/`)
- The round-trip oracle now treats null and empty arrays, and null/empty
LocalizedText Text/Locale, as equivalent in every mode (5.1.11:
"semantically the same for all DataEncodings… applications shall treat
null and empty arrays as equal"). Previously this only held when
`IgnoreNullValues` was set.
- `FuzzCrashAssets` no longer prints a stack trace or an inline base64
reproducer for *tolerated* fidelity findings. Only the one-line summary
entry is reported, and it now names the actual cause instead of
`TargetInvocationException`. Before, tolerated findings used up the
60-per-target reproducer budget, so a genuine failure later in the
enumeration printed no reproducer. The externally supplied corpus inputs
were also written into the pipeline log on passing runs.
- Three hand-crafted curated reproducers (9–23 bytes, one per encoder
defect) were added under `Assets/Repo/`, which is always enforced
strictly. Each one fails on the previous master and passes with this
change.
### Reviewer notes: observable JSON output changes
- `LocalizedText("")` / `("en","")` now round-trip with `Text == null`
instead of `""` (equivalent per 5.1.11).
- Verbose and Compact scalar Variant output is unchanged.
`IgnoreNullValues` alone now also omits a NULL `Value`, as it does for
every other null field.
- A null *array* in a Variant is written as `"Value":[]` in every mode
instead of being dropped in Compact, and a null *matrix* is written as
`"Value":[]` instead of failing with `BadEncodingError`.
- Variants holding a Variant or DiagnosticInfo still reach the writer
and are rejected as before (`WriteBadVariantThrows`).
### Testing (net10.0, locally)
- Opc.Ua.Types.Tests 10,542 passed, including new JsonEncoder tests for
each rule above
- Opc.Ua.Core.Encoders.Tests 2,929 passed (one expectation updated from
`"Text":""`)
- Opc.Ua.PubSub.Tests 1,369 passed; Opc.Ua.Core.Tests JSON subset 83
passed; Opc.Ua.Aot.Tests 178 passed
- Opc.Ua.Encoders.Fuzz.Tests 8,112 passed; crash-asset tests in the
Certificates/Network/PubSub fuzz projects and Opc.Ua.Fuzzing.Tests
passed
- net48: `Opc.Ua.Encoders.Fuzz.Tests` builds with 0 warnings; net48
tests were not run locally
## Related Issues
- No tracking issue; found by triaging the fuzz crash-asset replay
output of the master pipeline.
## Checklist
- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [ ] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|
|
331a71ac2c |
Fix AI model version and inference argument handling (#4512)
## Summary
- Derive the AI server''s advertised specification version from
generated NodeSet metadata (new source-generated `ModelVersions.Target`
constant), so `AINodeManager.SpecificationVersion` can no longer drift
from the shipped model.
- Preserve inference parameters through inline, asynchronous, fallback,
and oversized-transfer execution instead of silently discarding them.
- Reject unsupported `PayloadUri` input explicitly with
`BadNotSupported` before any backend call, job creation, or transfer
allocation.
- Apply the supported `temperature` / `max_tokens` / `top_p` parameters
in both built-in backends via a shared parser.
Fixes #4505
Fixes #4506
## Review follow-ups (
|
||
|
|
d7dd1b0369 |
Fix telemetry component assembly identification (#4490)
# Description Telemetry extensions resolved meters and activity sources as `Opc.Ua.Types`, with results dependent on helper inlining. This prevented reliable attribution to the emitting component. - **Existing telemetry contract**: Add `CreateMeter(Assembly)` and `GetActivitySource(Assembly)` directly to `ITelemetryContext`. The separate `IAssemblyTelemetryContext` interface has been removed. - **Reliable component attribution**: Capture the caller assembly in non-inlined `CreateMeter`, `GetActivitySource`, and `StartActivity` extensions, then forward it to the existing telemetry context. - **Call-site routing and fallback**: Keep non-null meter calls on `telemetry.CreateMeter()`. Use explicit `TelemetryExtensions.CreateMeter(telemetry)` for nullable-context fallback. Preserve default meter/activity-source fallback when a context returns null. - **Coverage and documentation**: Cover caller-assembly naming, direct-context and extension routing, activity creation, and null-result fallback. Update diagnostics examples, OpenTelemetry filters, and migration guidance. **Compatibility:** Direct implementations of `ITelemetryContext` must implement the two new assembly-aware members. `TelemetryContextBase` supplies both. This interface addition is documented in the 2.0 migration guide; legacy implementer compatibility is not claimed. ## Local validation The Release build and focused `TelemetryExtensionsTests` fixture pass on both target frameworks: | Target | Result | | --- | --- | | net10.0 | Passed: 6, Failed: 0, Skipped: 0 | | net48 | Passed: 6, Failed: 0, Skipped: 0 | Both builds emit existing CA5394 warnings in `NodeStateChildNameIndexTests.cs`, which is unchanged by this PR. The full solution test suite was not run during this feedback pass. ## Related Issues ## Checklist - [ ] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #4483 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
f5ad124891 |
Additional fuzzing plus fixes for every defect the campaigns found (#4459)
# Description
Brings the stack's fuzz testing to parity with the fork
(`aio-opcua-sdk`), then fixes every defect the resulting campaigns
exposed.
The fork's fuzzing was used as the behavioural baseline: all **28
fork-local callbacks** and **13 OneFuzz registrations** are now
accounted for by an explicit mapping, not just by a larger target count.
The work extends the existing SharpFuzz/NUnit infrastructure rather than
introducing another framework.
### Coverage
- **84 continuous libFuzzer targets** and **81 AFL adapters** across
Encoders, Certificates, Network and PubSub.
- **PubSub area integrated** (Corpus/Tools/Tests siblings, solution
entries) — it previously had host source only and was absent from the
solution and replay infrastructure.
- Segmented binary adapters, binary-to-JSON and XML-to-JSON conversions,
standalone certificate loading, both chain-parser variants, CRL encoding
and canonical TBS round-trips.
- The 12 original fork message inputs are retained byte-identically
alongside richer deterministic generators; the three historical
crash/slow/timeout reproducers are unchanged (verified identical by
SHA-256).
### Production fixes found by fuzzing
These are real robustness and data-integrity bugs, each with a retained
regression input:
- `UadpBinaryReader.Advance` accepted attacker-controlled lengths on the
receive path. Declared payload sizes are now authoritative bounded
sections, with negative-count rejection and overflow-safe reassembly
arithmetic.
- PubSub JSON decoding leaked `InvalidOperationException` for invalid
UTF-8.
- `JsonEncoder.WriteExtensionObject` leaked
`System.Text.Json.JsonReaderException`.
- `JsonEncoder` silently emitted a lossy `{}` for an `ExtensionObject`
whose TypeId could not be represented in JSON.
- `JsonDecoder` wrapped a bodyless `ExtensionObject` envelope as a raw
JSON body instead of preserving `ExtensionObjectEncoding.None`.
- `Node.Encode` lost the null/empty collection distinction.
- Binary/XML Variant matrix dimensions are prevalidated rather than
relying on a caught `ArgumentException`.
- `X509CRL.ReadTime` pre-validates strict RFC 5280 whole-second Zulu
time forms.
- `Utils.ParseCertificateChainBlob` disposes correctly on partial-chain
failure.
- **`nodeid.dict` was unloadable by libFuzzer** (empty token, unescaped
quote). libFuzzer rejects the whole file on one bad line, so four parser
targets silently executed **zero** inputs. Fixed and now guarded by
dictionary validation.
### Infrastructure
- `fuzzing/fuzz-targets.json` manifest recording adapters, corpora,
dictionaries, oracles and fork-behaviour mappings, with
`validate-fuzz-manifest.ps1` and **27 negative controls**.
- Strict host/tools discovery and replay: `--list`, `--replay`, non-zero
exits on unknown targets, missing or empty corpora, and failed callbacks
(playback previously logged failures and returned success).
- A real process watchdog for the timeout/slow regressions — NUnit
`CancelAfter` cannot interrupt a synchronous fuzz callback.
- Runner scripts made non-destructive (unique work directories, no
generic `bin`/findings deletion) and corrected to the native driver's
single-`--target_arg` apphost contract.
- Opt-in `OneFuzz=true` class-library publication producing a
relocatable, **uninstrumented** drop, validated by exact callback
discovery, dependency closure, corpora and dictionaries.
- CI: fuzz replay matrix plus a public drop gate, triggered by corpus-,
dictionary- and script-only changes.
### Verification
| Gate | Result |
|---|---|
| Bounded campaigns, all 84 targets (1,000 inputs, seed 1) | 84/84,
84,000 executions, zero findings |
| OneFuzz drop | 84/84 published callbacks replayed, zero skips |
| Fuzz replay net10.0 / net8.0 / net9.0 | 9,470 passed each, 0 failed |
| Fuzz replay net48 | 8,996 passed, 0 failed (Network/PubSub are
modern-only) |
| `Opc.Ua.Types.Tests` / `Opc.Ua.Core.Tests` | 9,800 and 4,442 passed, 0
failed |
| Retained regression assets | 132 `crash-*` inputs replayed by every
target each run |
### Known gaps (deliberately not closed)
- **Fidelity is not enforced against the pipeline crash corpus.** The
differential binary-to-JSON targets assert both robustness (no unhandled
exception, expected of arbitrary bytes) and fidelity (a decoded value
re-encodes equivalently, a property of well formed values). The pipeline
supplies 22,293 blobs collected for the targets that existed before this
PR, none of which asserted fidelity; 522 of them violate it, and that
count did not move across two rounds of genuine fixes because each fix
only lets the mutator reach the next representability gap. Rather than
weaken the assertion, the two contracts are separated: fidelity failures
raise `EncodingFidelityException`; curated assets under `Assets/Repo`,
the curated `Testcases` corpus and every continuous libFuzzer campaign
stay strictly enforced, while externally supplied assets are gated on
robustness only and their fidelity findings are reported with counts and
bounded base64 reproducers.
- **Internal MSFT OneFuzz canary has not been run.** It needs an
owner-supplied ownership profile, pipeline authorisation and corpus
snapshot; none were available. The drop builds and validates locally and
records `serviceCanaryPerformed: false`. A build or upload is not a
successful worker run.
- **Legacy JSON wire forms are a partial mapping.** The fork accepts its
four original `TypeId`/`Body` messages; this stack rejects that retired
envelope (measured JSON-only control: fork `JsonEncoder` 630/1356 lines
vs 0/1007 here). Recorded as an explicit unresolved mapping rather than
an exemption. `LegacyNonReversible` is a RawData-derived profile and is
not counted as independent coverage.
## Related Issues
_No tracking issue was supplied for this work; happy to open one and
link it if maintainers prefer._
- Fixes #github-issue-number, ...
## Checklist
- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [x] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
> Note on the checklist: the fuzz replay matrix, `Opc.Ua.Types.Tests`
and `Opc.Ua.Core.Tests` were run locally on **net48** and **net10.0**
(plus net8.0/net9.0) and all passed. CI and CodeQL results are not yet
available for this branch, so those boxes are left unchecked. The CLA
box is for the author to confirm.
|
||
|
|
7cec2d614d | Move repository files to the consolidated layout |