Previously when copying the allocator state we would copy an incorrect
root pointer into the document's current state; while this had a minimal
impact on the allocation state due to the fact that any new allocation
would need to create a new page, this used a potentially stale field of
the moved document when setting up new pages, which could create issues
in future uses of the pages.
This change fixes the core problem and also removes the use of the
_root->allocator from allocate_page since it's not clear why we need it
there in the first place.
Since foo//bar//baz adds two nodes for each //, we need to increment the
depth by 2 on each iteration to limit the AST correctly.
Fixes the stack overflow found by cluster-fuzz (I suspect the issue
there is a bit deeper, but this part is definitely a bug and as such I'd
rather wait for the next test case for now).
We now use open_file similarly to open_file_wide, and activate the
workaround for MSVC 2005+ since that's when the _s versions were added
in the first place.
Function call arguments are stored in a list which is processed
recursively during optimize(). We now limit the depth of this construct
as well to make sure optimize() doesn't run out of stack space.
The default stack on MSVC/x64/debug is sufficient for 1692 nested
invocations only, whereas on clang/linux it's ~8K...
For now set the limit to be conservative.
XPath parser and execution engine isn't stackless; the depth of the
query controls the amount of C stack space required.
This change instruments places in the parser where the control flow can
recurse, requiring too much C stack space to produce an AST, or where a
stackless parse is used to produce arbitrarily deep AST which will
create issues for downstream processing.
As a result XPath parser should now be fuzz safe for malicious inputs.
In some MSVC versions on x64 configurations, the hashing function
triggers this failure:
Run-Time Check Failure #1 - A cast to a smaller data type has caused a
loss of data. If this was intentional, you should mask the source of
the cast with the appropriate bitmask.
This is similar to the integer sanitizer - this code is valid C++ but
MSVC decides to warn about this nonetheless. Masking the pointer's low
32 bits fixes the issue.
Fixes#357.
The loop traverses the source tree and simultaneously builds up a copy
of it at destination. Short of race conditions, this code is safe -
however, it's not obvious that dit stays inside the destination tree.
This change adds a few assertions to help enforce/document these
invariants. One particular subtlety is that dit can actually *become*
null after we exit out of the loop, but it's guaranteed to only do so
once sit goes back to sn.
This is only possible when doing a full document copy - for some reason
we weren't using this for that (in reset(xml_document)), but we are now.
Fixes#314.
We were previously relying on non-standard comment detection that is
supported by gcc/clang to avoid warnings about implicit fallthrough.
This can be solved using attributes but using them requires a lot of
compiler-specific detection logic because not all versions of gcc/clang
support them.
We don't *really* need to rely on fallthrough here - the type conversion
block can be located *after* the AST type switch instead, which means
that any AST type that has type ambiguity can fall back to that in the
future.
Fixes#331.
Instead of performing a late null check that is redundant and only
needed to silence clang static analysis warning, we pick the context as
a root / self node. This way the code is a bit less redundant and the
static analyzer is happy.
This keeps src/ folder clean of auxiliary files only required for
special builds; note that CMakeLists.txt already depends on scripts/
(specifically for pkgconfig setup).
The newly added tests make sure that during node/attribute destruction
we deallocate a few memory pages; this makes sure that we don't read
node data after it's being destroyed.
Also clean up formatting/style in the remove_* implementation a bit.
According to XML spec, > sometimes needs to be escaped in PCDATA (when
it occurs as a ]]> pattern), but it doesn't need to be escaped in
attribute values.
Contributes to #272.
When using double quotes for attributes, we don't need to escape '; when
using single quotes, we don't need to escape ".
This changes behavior to match 1.9 by default (where we don't escape ').
Contributes to #272.
Note: this chang also updates PUGIXML_VERSION macro to allow for
double-digit minor versions; this preserves the continuity of versions
so PUGIXML_VERSION >= 190 will still work.
This change adds format_attribute_single_quote flag that uses single quotes (`'`) instead of double quotes (`"`) for formatting attribute values.
Internal quotation marks are escaped using `"` and `'`.
Given an unsorted sequence, remove_duplicates would sort it using the
pointer value of attributes/nodes and then remove consecutive
duplicates.
This was problematic because it meant that the result of XPath queries
was dependent on the memory allocation pattern. While it's technically
incorrect to rely on the order, this results in easy to miss bugs.
This is particularly common when XPath queries use union operators -
although we also will call remove_duplicates in other cases.
This change reworks the code to use a hash set instead, using the same
hash function we use for compact storage. To make sure it performs well,
we allocate enough buckets for count * 1.5 (assuming all elements are
unique); since each bucket is a single pointer unlike xpath_node which
is two pointers, we need somewhere between size * 0.75 and size * 1.5
temporary storage.
The resulting filtering is stable - we remove elements that we have seen
before but we don't change the order - and is actually significantly
faster than sorting was.
With a large union operation, before this change it took ~56 ms per 100
query invocations to remove duplicates, and after this change it takes
~20ms.
Fixes#254.
This does not change the result of a union operation [substantially], but
it means that we now give a list to remove_duplicates that has more natural
ordering.
If remove_duplicates didn't sort the array, we'd have union operations
resulting in a consistent predictable order.
Contributes to #254.
We had a few places in test code and library source where we used an
implicit float->double cast; while it should preserve the value exactly,
gcc/clang implement this warning to make sure uses of double are intentional.
This change also adds the warning to Makefile to make sure we don't
regress on this warning.
Fixes#243.
This change modifies the table entries for ctx_special_attr to treat TAB
character as special, which makes the output code escape it.
Before this change, trying to use TAB in an attribute value would output
it verbatim; during subsequent parsing, pugixml - and other compliant
parsers - would apply attribute-value normalization, turning the TAB
into a space and losing the original value.
Using 	 fixes this; if an input document has 	 in an attribute
value, that gets unescaped into \t during parsing and escaped back into
	 during output, which means we can now roundtrip values like this.
Fixes#242.
Coverity hits a similar false positive to what clang static analyzer hit
- it assumes that since optimize() checks _right for being nullptr,
optimize_self() might hit _right=nullptr in the ast_op_equal case which
is impossible.
Contributes to #236.
The following warning is removed:
Visual Studio 14.0
1. warning C4275: non dll-interface class 'std::exception' used as
base for dll-interface class 'vtkpugixml::xpath_exception'
clang doesn't understand the invariants guaranteed for specific AST node
types and, when seeing null pointer checks in optimize(), assumes any
pointers in the node might be null. Work around this by adding explicit
- redundant - null pointer checks.
This change replaces xpath_node_set single element storage with a
single-element array in hopes that this would silence Coverity false
positive about getting a singleton pointer.
Additionally, it refactors _assign member to unify small and large
buffer codepaths since they are basically identical.
Fixes#233 (hopefully)
gcc-8 produces "attribute directive ignored" warning for
no_sanitize("unsigned-integer-overflow"); at some point gcc will
introduce integer sanitizer support and we'll have to do this all over
again but for now just don't emit the attribute.
We were using << compact_alignment_log2 instead of * compact_alignment
for symmetry with the encoding where >> is crucial to keep code fast and
round to negative infinity.
For decoding, the results are the same and any reasonable compiler
should convert *4 into <<2 so just use a multiplication - that doesn't
trigger UB on negative numbers.
We were using allocate_memory to allocate struct xml_extra_buffer that
contains pointers; with compact mode, this allocation can be misaligned
by 4b with 8b pointers; fix this by manually realigning the pointer.
We were misaligning document data on 64-bit platforms by placing 8b
pointers at 4b offsets; fix this by reserving a full pointer worth of
bytes for page marker.
Define noexcept using _MSC_VER instead of _MSC_FULL_VER (first release
of MSVC 2015 should have it), remove redundant PUGIXML_HAS_NOEXCEPT and
define PUGIXML_NOEXCEPT_IF_NOT_COMPACT in terms of PUGIXML_NOEXCEPT.
* Adds a macro definition to be able to use noexcept with supporting compilers
* Adds noexcept specifier to move special members of xpath_node_set, xpath_variable_set and xpath_query, but not of xml_document as it has a throwing implementation
Texas Instruments compiler produces this warning for unused template
member functions:
"pugixml.cpp", line 253: warning #179-D: function
"pugi::impl::<unnamed>::auto_deleter<T>::release [with
T=pugi::impl::<unnamed>::xml_stream_chunk<char>]" was declared but
never referenced
As far as I can tell, this is a compiler issue - these functions should
not be instantiated in the first place; while it's possible to rework
the code to work around this, the changes would be fragile. It seems
best to just disable this warning - we've seen something similar on SNC
(which appears to use the same frontend!..).
Fixes#182.
It looks like there are several cases where this might happen:
- In some MinGW distributions, the LLONG_MIN/etc defines are guarded
with:
#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
Which means that you don't get them in strict ANSI mode. The previous
workaround was specifically targeted towards this.
- In some GCC distributions (notably GCC 6.3.0 in some configurations),
LLONG_MIN/etc. defines are guarded with:
#if (defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
But __STDC_VERSION__ isn't defined as C99 even if you use -std=c++14 -
which is probably technically valid, but not useful.
To work around this, redefine the symbols whenever we are building with
GCC and we need them and they aren't defined - doing this is better than
not building. Instead of hard-coding the constants, use GCC-specific
__LONG_LONG_MAX__ to compute them.
Fixes#181.
The static_buffer optimization seems to come from the time where the
on-heap buffer was allocated using global memory operations. At this
point the temporary buffer and temporary string storage all come from
the evaluation stack (that can be partially allocated on heap...), so
the extra logic isn't relevant for performance.
In compact mode, we currently can not support zero-allocation moves
since some pointer assignments required during the move need to allocate
hash table slots.
This is mostly applicable to xml_document_struct::first_child, since the
pointer to this element is used as a hash table key, but there are some
contrived cases where parents of root's children need a hash slot and
didn't have it before.
These cases can be fixed by changing the compact encoding to be a bit
more move friendly, but for now it's easier to handle the error and
throw/return during move.
When this happens, the source document doesn't change.
After move some nodes in the hash table can have keys that point to
other; this makes the table somewhat larger but this does not impact
correctness.
The reason is that for us to access a key in the hash table, there
should be a compact_pointer/string object with the state indicating that
it is stored in a hash table, and with the address matching the key. For
this to happen, we had to have put this object into this state which
would mean that we'd overwrite the hash entry with the new, correct
value.
When nodes/pages are being removed, we do not clean up keys from the
hash table - it's safe for the same reason, and thus move doesn't
introduce additional contracts here.
This change implements the initial version of move construction and
assignment support for documents.
When moving a document to another document, we always make sure move
target is in "clean" state (empty document), and proceed by relocating
all structures in the most efficient way possible.
Complications arise from the fact that the root (document) node is
embedded into xml_document object, so all pointers to it have to change;
this includes parent pointers of all first-level children as well as
allocator pointers in all memory pages and previous pointer in the first
on-heap memory page.
Additionally, compact mode makes everything even more complicated
because some of the pointers we need to update are stored in the hash
table (in fact, document first_child pointer is very likely to be there;
some parent pointers in first-level children will be using
compact_shared_parent but some won't be) which requires allocating a new
hash table which can fail.
Some details of this process are not fully fleshed out, especially for
compact mode; and this definitely requires many tests.
It's not clear whether we still need PUGI__MSVC_CRT_VERSION, but it's
more consistent for now to use it for _snprintf_s since this is relying
on a CRT extension, not on a compiler feature.
These functions were deprecated via comments in 1.5 but never got the
deprecated attribute; now is the time!
Using deprecated functions produces a warning; to silence it, this
change moves the relevant tests to a separate translation unit that has
deprecation disabled.
The macro only works correctly when the input argument is an array with
a statically known size - pointers or arrays decayed to pointers won't
work silently.
While this is unlikely to surface issues that aren't caught in
tests/code review, use _countof for MSVC to prevent such code from
compiling.
Instead of branching code at each invocation site, use variadic macros
to create a wrapping macro that use snprintf for the buffer of a
statically known size.
Variadic macros are supported by all C++11 compilers, as is snprintf;
on MSVC 2005+ we don't necessarily have snprintf, but we can use
_snprintf_s with _TRUNCATE to get the same behavior. In all other cases
we fall back to sprintf, that (theoretically) can lead to a stack buffer
overflow.
In practice all snprintfs used in pugixml use buffers that should be
large enough to never be overflown but snprintf is safe even if this is
not the case.
We use references to arrays elsewhere in the codebase and there's just
one caller for this function so it's easier to fix the size.
This will simplify snprintf refactoring.
Integer sanitizer is flagging unsigned integer overflow in several
functions in pugixml; unsigned integer overflow is well defined but it
may not necessarily be intended.
Apart from hash functions, both string_to_integer and integer_to_string
use unsigned overflow - string_to_integer uses it to perform
two-complement negation so that the bulk of the operation can run using
unsigned integers. This makes it possible to simplify overflow checking.
Similarly integer_to_string negates the number before generating a
decimal representation, but negating is impossible without unsigned
overflow or special-casing certain integer limits.
For now just silence the integer overflow using a special attribute;
also move unsigned overflow into string_to_integer from get_value_* so
that we have fewer functions marked with the attribute.
Fixes#133.
This reverts commit 79109a8546f963d17522d75112cffcfd8cbe35fc.
This warning does not happen on gcc-4.8.4; the workaround introduces an
unsigned integer overflow which results in a runtime error when compiled
with integer sanitizer.
This is accomplished by putting a // fallthrough
comment at the right place.
This seems to be more portable than an attribute-based
solution like [[fallthrough]] or __attribute__((fallthrough)).
Instead of a separate implementation for find/insert, use just one that
can do both. This reduces the code size and simplifies code coverage;
the resulting code is close to what we had in terms of performance and
since hash table is a fall back should not affect any real workloads.
Instead of a complicated partitioning scheme that tries to maintain the
equal area in the middle, use a scheme where we keep the equal area in
the left part of the array and then move it to the middle.
Since generally sorted arrays don't contain many duplicates this extra
copy is not too expensive, and it significantly simplifies the logic and
maintains good complexity for sorting arrays with many equal elements
nonetheless (unlike Hoare partitioning).
Instead of a median of 9 just use a median of 3 - it performs pretty
much identically on some internal performance tests, despite having a
bit more comparisons in some cases.
Finally, change the insertion sort threshold to 16 elements since that
appears to have slightly better performance.
The previous implementation opted for doing two comparisons per element
in the sorted case in order to remove one iterator bounds check per
moved element when we actually need to copy. In our case however the
comparator is pretty expensive (except for remove_duplicates which is
fast as it is) so an extra object comparison hurts much more than an
iterator comparison saves.
This makes sorting by document order up to 3% faster for random
sequences.
Instead of relying on a specific string in the parse result, use
allocator error state to report the error and then convert it to a
string if necessary.
We currently have to manually trigger the OOM error in two places
because we use global allocator in rare cases; we don't really need to
do this so this will be cleaned up later.
The code works fine regardless of the *j->name check, and omitting this
makes the code more symmetric between the "count" and "write" stage;
additionally this improves coverage - due to how strcpy_insitu works
it's not really possible to get an empty non-NULL name in the node.
Instead of rolling back the allocation and trying to allocate again,
explicitly handle inplace reallocate if possible, and allocate a new
block otherwise.
This is going to be important once we use reallocate_nothrow from a
non-throwing context.
This allows us to gradually convert exception handling of out-of-memory
during evaluation to a non-throwing approach without changing the
observable behavior.