Skip to content

Fix some security issues and stability for data types - #477

Merged
neuecc merged 6 commits into
mainfrom
feature/hardening-and-compat-types
Nov 11, 2026
Merged

neuecc merged 6 commits into
mainfrom
feature/hardening-and-compat-types

Conversation

@neuecc

@neuecc neuecc commented Nov 11, 2026

Copy link
Copy Markdown
Member

Hardens the reader and formatters (string, multi-dimensional array, BrotliString, BitArray) against malformed data.
Adds opt-in fixed-layout types Net10DateTimeOffset, UnityDateTimeOffset and SafeNullable<T>, plus the MEMPACK043 warning for DateTimeOffset members. Wire format of valid data is unchanged.

Related: #473, #475, #460, #306, #422, #399, #192, #381

neuecc and others added 6 commits November 11, 2026 10:57
- MemoryPackReader.GetSpanReference/Advance reject negative size
  (string header overflow, negative skip of version tolerant object)
- byte count calculation of unmanaged array/span is checked
- multi dimensional array validates dimensions by collection length before allocate
- BrotliStringFormatter checks overflow of byte length before the size limit check
- BitArrayFormatter uses public API when the private field layout of BitArray
  is not matched to BitArrayView (.NET 10 or later), binary format is not changed

Valid binary and wire format are not changed.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
DateTimeOffset is serialized as its memory layout but it depends on runtime.
- .NET 9 or lower: (short offsetMinutes, long utcTicks)
- .NET 10 or later: (int offsetMinutes, long utcTicks)
- Unity: (long utcTicks, short offsetMinutes)

Net10DateTimeOffset and UnityDateTimeOffset are fixed layout structs that
can be converted from/to DateTimeOffset implicitly.
- Net10DateTimeOffset: same as .NET 10 or later, can also read the binary of .NET 9 or lower
- UnityDateTimeOffset: same as Unity

When target framework is lower than .NET 10, source generator reports
warning MEMPACK043 for members that use DateTimeOffset.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Nullable<T> of unmanaged type is serialized as its memory layout
(bool hasValue, padding, T value) and the padding is not initialized,
so the binary of the same value is not always same.

SafeNullable<T> is (T hasValue, T value), it has no padding.
When T is primitive or enum, the binary is compatible with Nullable<T>.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…tters

Remove the view types that reinterpret BCL collections by Unsafe.As
(ListView, StackView, ImmutableArrayView, BitArrayView), the private
field layout is not guaranteed and differs by runtime.

- List<T>: CollectionsMarshal.SetCount and AsSpan in .NET 8 or later,
  Add one by one in net7.0 and netstandard2.1.
- Stack<T>: CopyTo the pooled array to serialize, constructor or Push
  to deserialize.
- ImmutableArray<T>: read to the pooled array and ImmutableArray.Create
  in net7.0 and netstandard2.1, .NET 8 or later is not changed.
- BitArray: CopyTo(int[]) and new BitArray(int[]) in all runtimes,
  clear unused bits of the last element, reject the length that is not
  matched to the array.

Binary format is not changed.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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