Commit 6b82794
committed
fix(text-json): a JsonElement reports its document's disposal (#2117)
A JsonElement captured before JsonDocument::Dispose() kept answering GetInt32()=10. It now
raises ObjectDisposedException, as .NET does. sizeof(JsonElement) 48 -> 56; consumers rebuild.
THE GATE WAS SA-3 ALL ALONG. #2117's blocker was "enforcing it needs shared disposal state
reachable from every element, i.e. an OBJECT-LAYOUT CHANGE to JsonElement ... gated exactly as
modules/io's #2098 is" -- and #2098 landed under SA-3 (Approval IO-1) on 2026-08-18. SA-3 covers
this shape, so the gate is discharged rather than waived.
The review's framing correction stands: this was never a use-after-free. JsonElement held an
OWNING aliasing shared_ptr, so a captured element kept the tree alive and read LIVE storage. The
defect was a disposed document still serving data.
THE DESIGN IS .NET'S, NOT A WORKAROUND. .NET's JsonElement holds a JsonDocument _parent and an
index, and delegates every accessor to _parent.GetXxx(_idx); each of those begins with
CheckNotDisposed() -- some twenty call sites in JsonDocument.cs. The flag lives with the
DOCUMENT, and the element reaches it through the reference it already holds. This port's element
now points at a shared detail::JsonDocumentState and carries the node as a raw pointer into it --
the direct counterpart of _parent plus _idx.
The separate `bool disposed_` on JsonDocument is DELETED rather than mirrored: two flags for one
fact is what let the document and its elements disagree. Deriving a child element no longer
builds an aliasing shared_ptr at all, so five construction sites got simpler.
TWO BOUNDARIES ARE .NET'S AND BOTH ARE PINNED:
- A DEFAULT element is undefined, not disposed. .NET keeps them apart -- CheckValidInstance()
raises InvalidOperationException for a null parent, CheckNotDisposed() raises
ObjectDisposedException. Writing the guard as `if (!node_) throw` would fail that.
- A Clone() taken BEFORE disposal survives, because .NET's Clone() delegates to
_parent.CloneElement(_idx), producing an element rooted in a NEW document.
ValueKind throws too, which is easy to leave out and is not optional: .NET's reads
_parent.GetJsonTokenType, which begins with CheckNotDisposed().
Dispose() still retains the tree, and now buys something for it. .NET frees its buffer there,
but .NET's elements reference the DOCUMENT and are told they are disposed; this port's elements
reference the STATE and would read freed storage if it vanished under them. Retention is what
makes the diagnostic safe -- the cost the class note has recorded since #2110, now paid for a
check rather than for nothing.
Mutations: 5, all caught. Two pins inverted, each of which asserted the defect verbatim. The
layout pin uses shadow structs and asserts the two differ by EXACTLY one pointer, so a change to
any member's own size shows up as a mismatch rather than a number to re-guess.
Gate: 17,388 run, 17,388 passed, 0 failed, 0 skipped across 38 executables (+2, in
SharpRuntimeTests_Text_Json, 298 -> 300). Module graph unchanged at 41/93. No vtable,
base-class, signature or noexcept change. Built in build/ with --parallel 2.
Downstream: zero JsonElement/JsonDocument code sites in cna and mobile-eggbert.
docs/Migration-JsonElementDisposalGuard.md1 parent cd45b1a commit 6b82794
8 files changed
Lines changed: 272 additions & 36 deletions
File tree
- docs
- modules/text-json
- include/System/Text/Json
- detail
- src/System/Text/Json
- tests/System/Text/Json
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
Lines changed: 16 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
35 | | - | |
36 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
37 | 40 | | |
38 | | - | |
| 41 | + | |
39 | 42 | | |
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
43 | 46 | | |
44 | 47 | | |
45 | | - | |
46 | | - | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
47 | 55 | | |
48 | 56 | | |
49 | 57 | | |
50 | 58 | | |
51 | | - | |
52 | | - | |
| 59 | + | |
| 60 | + | |
53 | 61 | | |
54 | 62 | | |
55 | 63 | | |
| |||
62 | 70 | | |
63 | 71 | | |
64 | 72 | | |
65 | | - | |
| 73 | + | |
66 | 74 | | |
67 | 75 | | |
68 | 76 | | |
| |||
Lines changed: 55 additions & 12 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
11 | 13 | | |
12 | 14 | | |
13 | 15 | | |
| |||
27 | 29 | | |
28 | 30 | | |
29 | 31 | | |
30 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
31 | 40 | | |
32 | 41 | | |
33 | 42 | | |
34 | 43 | | |
35 | 44 | | |
36 | 45 | | |
37 | 46 | | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
38 | 66 | | |
39 | 67 | | |
40 | 68 | | |
41 | | - | |
42 | | - | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
43 | 72 | | |
44 | | - | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
45 | 79 | | |
46 | | - | |
| 80 | + | |
47 | 81 | | |
48 | 82 | | |
49 | 83 | | |
| |||
63 | 97 | | |
64 | 98 | | |
65 | 99 | | |
66 | | - | |
| 100 | + | |
67 | 101 | | |
68 | 102 | | |
69 | 103 | | |
| |||
92 | 126 | | |
93 | 127 | | |
94 | 128 | | |
95 | | - | |
| 129 | + | |
96 | 130 | | |
97 | 131 | | |
98 | 132 | | |
| |||
110 | 144 | | |
111 | 145 | | |
112 | 146 | | |
113 | | - | |
| 147 | + | |
114 | 148 | | |
115 | 149 | | |
116 | 150 | | |
| |||
124 | 158 | | |
125 | 159 | | |
126 | 160 | | |
127 | | - | |
| 161 | + | |
128 | 162 | | |
129 | 163 | | |
130 | 164 | | |
| |||
157 | 191 | | |
158 | 192 | | |
159 | 193 | | |
160 | | - | |
| 194 | + | |
161 | 195 | | |
162 | 196 | | |
163 | 197 | | |
164 | 198 | | |
165 | 199 | | |
166 | 200 | | |
167 | 201 | | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
168 | 210 | | |
169 | | - | |
170 | | - | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
171 | 214 | | |
172 | 215 | | |
173 | 216 | | |
| |||
Lines changed: 40 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
96 | | - | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
| |||
0 commit comments