Should print "invalid JSON". Instead crashes with a NullReferenceException.
Analysis
The crash happens on the expression _parent!.Parent, so the damnit operator here is inaccurate.
The sequence of events is:
JTokenWriter.WriteStartConstructor invokes AddParent(new JConstructor(name)) with an empty name. This throws an ArgumentException, so AddParent is not invoked, even though WriteStartConstructor has successfully been called
JsonWriter.Dispose is invoked, which does Close → AutoCompleteAll → WriteEnd → WriteEndConstructor → InternalWriteEnd → AutoCompleteClose → JTokenWriter.WriteEnd → JTokenWriter.RemoveParent, removing a level of parenting that doesn't exist
Proposed fixes?
Moving the new JConstructor call to the start of WriteStartConstructor validates the name before WriteStartConstructor is invoked. This stops the NRE but still raises an ArgumentException instead of a JsonException. Probably the validation of the constructor name should happen much earlier, during reading not writing.
Also, should JsonWriter be completing its work if there is an exception in-flight?
ArgumentException
It is also possible to surface the ArgumentException itself via:
NullReferenceException
Sample code
Expected/actual behavior
Should print "invalid JSON". Instead crashes with a
NullReferenceException
.Analysis
The crash happens on the expression
_parent!.Parent
, so the damnit operator here is inaccurate.The sequence of events is:
JTokenWriter.WriteStartConstructor
invokesAddParent(new JConstructor(name))
with an emptyname
. This throws anArgumentException
, soAddParent
is not invoked, even thoughWriteStartConstructor
has successfully been calledJsonWriter.Dispose
is invoked, which doesClose → AutoCompleteAll → WriteEnd → WriteEndConstructor → InternalWriteEnd → AutoCompleteClose → JTokenWriter.WriteEnd → JTokenWriter.RemoveParent
, removing a level of parenting that doesn't existProposed fixes?
Moving the
new JConstructor
call to the start ofWriteStartConstructor
validates the name beforeWriteStartConstructor
is invoked. This stops the NRE but still raises anArgumentException
instead of aJsonException
. Probably the validation of the constructor name should happen much earlier, during reading not writing.Also, should
JsonWriter
be completing its work if there is an exception in-flight?ArgumentException
It is also possible to surface the
ArgumentException
itself via:Expected/actual behavior
Should print "invalid JSON". Instead crashes with a
ArgumentException
("Constructor name cannot be empty").Analysis
Underlying cause is the same as previous but this one avoids the call to
Dispose
so it doesn’t trigger the NRE.Credit: Found via LibFuzzer & SharpFuzz.