Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internal error while xml serializing a byte[] property set to null #421

Closed
lscorcia opened this issue Dec 1, 2019 · 15 comments · Fixed by #54800
Closed

Internal error while xml serializing a byte[] property set to null #421

lscorcia opened this issue Dec 1, 2019 · 15 comments · Fixed by #54800
Assignees
Milestone

Comments

@lscorcia
Copy link
Contributor

lscorcia commented Dec 1, 2019

Hi, I am evaluating the porting of some .net 4.7.2 code to .net core 3 and found this problem that I think is a compatibility issue in XML Serialization.

You can find the repro project at https://github.com/lscorcia/net-core-serialization-issue.

Basically we have a WCF Connected Service which takes a couple of objects as parameters. One of them contains a byte[] property, declared as nullable in the service definition. This is the relevant generated proxy snippet:

/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute(DataType="base64Binary", IsNullable=true)]
public byte[] contenuto
{
  get
  {
    return this.contenutoField;
  }
  set
  {
    this.contenutoField = value;
  }
}

When invoking the remote service, the request serialization is ok when the property is set to some value, but fails when the value is null: it throws a System.ServiceModel.CommunicationException in System.Private.ServiceModel.dll. Here is the full stack trace:

System.ServiceModel.CommunicationException
  HResult=0x80131500
  Message=There was an error in serializing body of message : 'There was an error generating the XML document.'.  Please see InnerException for more details.
  Source=System.Private.ServiceModel
  StackTrace:
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)

Inner Exception 1:
InvalidOperationException: There was an error generating the XML document.
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id) in /_/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs:line 373
   at System.ServiceModel.Dispatcher.XmlSerializerOperationFormatter.SerializeBody(XmlDictionaryWriter writer, MessageVersion version, String action, MessageDescription messageDescription, Object returnValue, Object[] parameters, Boolean isRequest)

Inner Exception 2:
InvalidOperationException: Internal error.
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WritePrimitiveValue(TypeDesc typeDesc, Object o, Boolean isElement, String& stringValue) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 1165
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WritePrimitive(WritePrimitiveMethodRequirement method, String name, String ns, Object defaultValue, Object o, TypeMapping mapping, Boolean writeXsiType, Boolean isElement, Boolean isNullable) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 991
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteElement(Object o, ElementAccessor element, Boolean writeAccessor) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 405
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteElements(Object o, Object enumSource, ElementAccessor[] elements, TextAccessor text, ChoiceIdentifierAccessor choice, Boolean writeAccessors, Boolean isNullable) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 194
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteStructMethod(StructMapping mapping, String n, String ns, Object o, Boolean isNullable, Boolean needType) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 598
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.<>c__DisplayClass20_0.<CreateXmlSerializationWriteCallback>b__0(Object o) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 456
   at System.Xml.Serialization.XmlSerializationWriter.WriteReferencedElement(String name, String ns, Object o, Type ambientType) in /_/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs:line 1350
   at System.Xml.Serialization.XmlSerializationWriter.WriteReferencedElements() in /_/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationWriter.cs:line 1370
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.GenerateMembersElement(Object o, XmlMembersMapping xmlMembersMapping) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 1371
   at System.Xml.Serialization.ReflectionXmlSerializationWriter.WriteObject(Object o) in /_/src/System.Private.Xml/src/System/Xml/Serialization/ReflectionXmlSerializationWriter.cs:line 74
   at System.Xml.Serialization.XmlSerializer.SerializeUsingReflection(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id) in /_/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs:line 383
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id) in /_/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs:line 350

This type of call was working fine in the original .net 4.7.2 software.
I can supply the service WSDL if needed, but I don't think that's relevant - the WCF proxy class looks ok and the error is in the early request creation.

I tried source-stepping in the framework code, but it is heavily optimized and the debugger is not that helpful. It looks like the null value of the array prevents the recognition of the parameter as an array and that it ends up on the wrong code path, but I might be easily mistaken on this.

Please let me know if you need any additional information.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-Serialization untriaged New issue has not been triaged by the area owner labels Dec 1, 2019
@lscorcia
Copy link
Contributor Author

lscorcia commented Dec 4, 2019

The issue is still present on the recently released .net core 3.1.0 . I have updated the example project in the repository.

@StephenBonikowsky
Copy link
Member

@lscorcia Thanks for reporting this, we will be investigating and will follow-up when we have more info.

@StephenBonikowsky StephenBonikowsky removed the untriaged New issue has not been triaged by the area owner label Dec 9, 2019
@StephenBonikowsky StephenBonikowsky added this to the 3.1.x milestone Mar 5, 2020
@HongGit HongGit modified the milestones: 3.1.x, 6.0.0 Oct 1, 2020
@HongGit
Copy link
Contributor

HongGit commented Oct 1, 2020

Moving this to .NET 6.0 milestone, if needed, we will consider to backport to 3.1.

@GidanMX2
Copy link

GidanMX2 commented Oct 12, 2020

I have the same issue on 2.2, I have to pass an empty byte[] instead of null to bypass the problem

@ankit57
Copy link

ankit57 commented Mar 23, 2021

I am facing same issue in 3.1.0. Is there any update on the fix or workaround?

MichalStrehovsky pushed a commit to MichalStrehovsky/runtime that referenced this issue Mar 25, 2021
@lscorcia
Copy link
Contributor Author

lscorcia commented Apr 4, 2021

The issue is still present on .net core 5. I have updated the example project in the repository.
Maybe one day this will be fixed and I will no longer have a sad smile when the .net teams wonder why .net core adoption rates are lower than expectations (https://visualstudiomagazine.com/articles/2021/03/16/ef-core.aspx).

@danmoseley
Copy link
Member

@lscorcia are you willing to debug/investigate a little further to see why .NET Core is behaving differently?

@lscorcia
Copy link
Contributor Author

lscorcia commented Apr 5, 2021

Sure, let me know what I can do!

@danmoseley
Copy link
Member

This isn't actually my area (that's @HongGit team see area-owners.md) but I was assuming you could attach a debugger and see what's going on. Often when there's a regression from .NET Framework it's quite fixable, although again I'm not familiar with this library.

@danmoseley
Copy link
Member

Instructions for building and debugging are linked from the README.

@lscorcia
Copy link
Contributor Author

lscorcia commented Apr 5, 2021

So, if I understand correctly, the process would be to create a debug build from the current repo. This would mean preparing the build requirements (vs2019 c++ workload, cmake, etc), cloning the repo at the .net 5 tag, building the full runtime (not just the libraries), then find out how to connect a vs2019 debugger to the built environment and then finally try to understand where the crash comes from. It's going to be a very long process as I will need to do that in a VM as I can't risk borking my work environment, and my machine is a crappy laptop. I will try, but I can't guarantee I will be successful. If you know of any prebuilt debug build somewhere it's probably going to be much easier.

@danmoseley
Copy link
Member

@lscorcia I should not have said building. I was thinking you could simply attach Visual Studio (any recent version) choosing the ".NET 5" debugger engine, set a function breakpoint on System.Xml.Serialization.ReflectionXmlSerializationWriter.WritePrimitiveValue (or on InvalidOperationException and hit ignore if it hits any in another library). When the breakpoints hits, the debugger should be able to pull down sources and symbols automatically. Then you could maybe look at the local state and make some inferences.

@HongGit is there someone on your team who could give more specific pointers? It seems this is an upgrade blocker for @lscorcia .

@lscorcia
Copy link
Contributor Author

lscorcia commented Apr 5, 2021

Hi @danmoseley , I did what you suggested and I was able to source-step inside the framework code. The exception is triggered at

else
{
throw new InvalidOperationException(SR.XmlInternalError);
}
when the method WritePrimitiveValue is invoked with the following parameters:

  • typeDesc = System.Byte[] (FormatterName = "ByteArrayBase64")
  • o = null

Now, at

else if (o is byte[] && typeDesc.FormatterName == "ByteArrayHex")
we see an explicit check for a different Formatter, so the code does not enter that path, and also at
else if (o is byte[])
{
// we deal with byte[] specially in WritePrimitive()
}

there is a check for byte[], but it only checks the value object type, not the typeDesc, therefore it does not enter that path either (o is an object with null value).

This latter if block has a comment that suggests that the byte[] should be dealt with in the caller method WritePrimitive, but then the exception is raised and it never reaches that point:

I am not familiar with the design of this serialization logic, but maybe the issue is that those checks are being performed on the value instead of using the type descriptor typeDesc? The fix could be to simply check the typeDesc in case the value is null?

@danmoseley
Copy link
Member

What would be interesting is to now figure out what .NET Framework is doing differently. Again using a debugger but you might have to step some more or get creative with how you break in, to find the equivalent point (since it's not throwing)

@danmoseley
Copy link
Member

Never mind, it seems this codepath may be new in .NET Core: dotnet/corefx#11369

I think we need an area expert to advise now.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jun 28, 2021
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jul 13, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Aug 12, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants