In Praise of Draconian Error Handling, Part 2
The fundamental reason to prefer draconian error handling is because it helps find bugs. I was recently reminded of this when Peter Murray-Rust thought he had found a bug in XOM. In brief, it was refusing to parse some files other tools let slip right through. In fact, XOM’s strict namespace handling had uncovered a cascading series of bugs that had been missed by various other parsers including Xerces-2j and libxml.
But before I describe what happened, let’s see if you can eyeball this bug. I’ll make it easier by cutting out the irrelevant parts so you know you’re looking right at the bug. Here’s the instance document we start with:
<!DOCTYPE svg SYSTEM "http://www.w3.org/TR/2000/03/WD-SVG-20000303/DTD/svg-20000303-stylable.dtd"> <svg/>
And the referenced DTD is:
<!ENTITY % StylableSVG "INCLUDE" > <!ENTITY % ExchangeSVG "IGNORE" > <!ENTITY % SVGNamespace "http://www.w3.org/2000/svg-20000303-stylable" > <!ENTITY % Shared PUBLIC "-//W3C//DTD SVG 20000303 Shared//EN" "svg-20000303-shared.dtd" > %Shared;
Then in svg-20000303-shared.dtd we find this:
<!ATTLIST svg xmlns CDATA #FIXED "%SVGNamespace;" %stdAttrs; >
Not obvious, is it? In fact, I looked at this one for quite a while, and consulted several spec documents before Tatu Saloranta figured out what was actually wrong here. If it helps the relevant part of the XML specification is Section 4.4, XML Processor Treatment of Entities and References.
Give up? OK. Here’s what’s happening:
Although the parameter entity reference %SVGNamespace;
appears in a DTD. it appears inside a default attribute value. The parameter entity reference is therefore not resolved until the attribute actually appears in the document. However, parameter entity references are not resolved in document content (including attribute values). There it’s just literal text. In essence this document is:
<svg xmlns="%SVGNamespace;"/>
Although this bug was nigh-on impossible to find by eye, XOM picked it right up. It noticed that Xerces was setting a namespace URI to %SVGNamespace;
. XOM didn’t know what the namespace was supposed to be, but it knew that %SVGNamespace;
was not a legal absolute URL, and it refused to allow that. Hence the bug was flagged, and we eventually figured out where and how in the DTD the bug really was. And then we figured out that this was an old DTD from a working dfraft spec, and the tool that was having trouble should be upgraded to the new DTD anyway.
However no other XML library I’m aware of would have caught this because none of them make the check that a namespace URL should be absolute. Technically that’s only good practice, not a strict requirement of the spec. However it is a strict requirement for Infoset based specs such as XInclude and XProc. A document that uses non-absolute URLs does not have an Infoset, and therefore one cannot use XInclude or XProc on them, at least not with any confidence that the results will be consistent from tool to tool because the spec simply does not define how to handle this case.
In practice, I have yet to encounter an XML document that did use a relative namespace URI for any good reason. I have had people request an option not to reject documents that use non-absolute URIs for namespaces, and merely allow any string to be passed in. However, if I accepted that request, then XOM would no longer catch real bugs like this. Furthermore, document producers would have less incentive to generate correct documents. The more error correction tools perform, the more error correction they have to perform. Documents gradually deviate further and further from the spec, and tools try to keep up. However different tools implement different forms and amounts of error correction, until eventually we might as well not have a spec at all.
Instead of participating in this race to the bottom, XOM refuses to play. It will parse namespace well-formed XML documents only. If you want to parse something else, you can’t. Fix the documents or fix the process generating the malformed documents. XOM isn’t changing.
June 5th, 2009 at 9:31 am
Nice catch. Bravo to XOM!
June 5th, 2009 at 10:12 am
One of the nice things about writing open source software is that you can tell your users they are wrong. That’s one of the reasons open source software is often higher quality. However, sometimes I think you have to give up the good fight, because sticking to your principles is just making your users angry. I did that for example when I started supporting the Sun “jar:” URI scheme (which doesn’t follow the rules for URI schemes); I’m also slowly giving up on the issue of accepting Windows filenames where URIs are expected (partly because most of the library routines I rely on seem to allow them). So I think you’re right in theory, but sometimes practical compromises are needed.
June 5th, 2009 at 11:03 am
On the one hand, I completely agree with your approach here.
On the other hand, this is just one more thing that makes me want to run screaming from XML. This stuff is way more complicated than any Real Person(TM) is going to be able to deal with.
June 5th, 2009 at 1:10 pm
You should check out this thought experiment for a counterpoint to this attitude.
Summarized: yes, it helps you find bugs, but it also DoS’s you when these bugs surface (instead of gracefully degrading), and the bugs might not even be your bugs.
June 6th, 2009 at 1:34 am
This isn’t a code bug. It’s a spec bug. XML entities are evil. A good rule of thumb: anything tough to describe is probably wrong.
June 11th, 2009 at 4:08 pm
Your conclusion is wrong. Whereas it might be nice to have a mode which rejects all but perfect XML, saying that XOM won’t change when XML is less than perfect is basically telling your users to switch to other parsers than XOM. Imagine what would happen if I released a web browser that rejected everything except perfect HTML. It would not be able to render 90% of the web. Do you think anyone would want to use such a browser, even if it was “correct”?
I happen to really like XOM, but if I encountered a case such as this I’d simply switch to a different parser.
June 16th, 2009 at 7:35 pm
You’re all idiots. Use XOM to develop your internal XML apps. Sleep easy knowing that any XML generated by your internal apps will be processed by all of the more permissive parsers used by your partners. Cry a little bit because XML generated by your external partners may trigger errors on import. Get over it when your XOM driven preprocessing XML cleanup tool helps give you clean data for imports into your internal database without much overhead. Laugh to the bank as your competitors are dragged down by the inefficiencies in their systems, while your own processes run smooth as butter.
June 29th, 2009 at 6:05 am
This is a good approach.
October 28th, 2009 at 10:22 am
Actually, that isn’t even a legal relative reference, never mind a legal absolute URL: “%SV” isn’t decodable as a %-escape.