When printing a `GVariant`.
This introduces no functional changes, but should speed things up a
little bit when printing out arrays.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
If a variant is trusted, that means all its children are trusted, so
ensure that their checked offsets are set as such.
This allows a lot of the offset table checks to be avoided when getting
children from trusted serialised tuples, which speeds things up.
No unit test is included because this is just a performance fix. If
there are other slownesses, or regressions, in serialised `GVariant`
performance, the fuzzing setup will catch them like it did this one.
This change does reduce the time to run the oss-fuzz reproducer from 80s
to about 0.7s on my machine.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2841
oss-fuzz#54314
When dereferencing the first entry in the offset table for a tuple,
check that it doesn’t fall outside the bounds of the variant first.
This prevents an out-of-bounds read from some non-normal tuples.
This bug was introduced in commit 73d0aa81c2575a5c9ae77d.
Includes a unit test, although the test will likely only catch the
original bug if run with asan enabled.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2840
oss-fuzz#54302
musl doesn’t define them itself, presumably because they’re not defined
in POSIX. glibc does define them. Thankfully, the values used in glibc
match the values used internally in other musl macros.
Define the values as a fallback. As a result of this, we can get rid of
the `g_assert_if_reached()` checks in `siginfo_t_to_wait_status()`.
This should fix catching signals from a subprocess when built against
musl.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2852
In case we're out of memory we should abort after having printed an error
message, this is similar to crashing but not exactly the same, so ensure
we exit for SIGABRT and not because of a SIGSEV.
We use a test wrapper to ensure that both the exit code and the stderr
match what we expected.
During tests in which we are isolating directories, we may still create
temporary files in the global temporary directory without cleaning them
because the value returned by g_get_tmp_dir() is cached when we isolate
the tests directory to the global TMPDIR.
To ensure that we're always isolating the temporary directories, let's
unset the cached temporary directory once we've defined $G_TEST_TMPDIR
so that the returned value of g_get_tmpdir() can be recomputed using the
test isolated temporary directory.
It allows to create a GPtrArray from a null-terminated C array computing its
size and in case performing copies of the its values using the provided
GCopyFunc.
GPtrArray is a nice interface to handle pointer arrays, however if a classic
array needs to be converted into a GPtrArray is currently needed to manually
go through all its elements and do new allocations that could be avoided.
So add g_ptr_array_new_take() which steals the data from an array of
pointers and allows to manage it using the GPtrArray API.
Reverts the following commits:
- ab621e15b52a57c8d95b3f4d93493c82f0f3216e.
- 85d9fb8e6c482d7b6d59efbf98040ad58d3f5008.
Too many build breaking regressions.
Fixes: #2846
This prevents stalls/deadlocks/timeouts on macOS. I don’t know why, as I
don’t have access to a macOS machine to test — this MR was put together
via testing on CI.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
This regressed in commit 9f558a2c5017860c92d69396d36dc7a6b6a4e2af.
Not sure if it makes a functional difference to the test, though.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
It has always been considered an unsigned value, and we also returned it
straight as int in g_hash_table_size(), but it was actually used as an
int.
So use the same type of g_hash_table_size(). Not using more standard
unsigned not to risk that it may different from the guint typedef.
Add functions to steal all the keys or values from a ghash (especially
useful when it's used as a set), passing the ownership of then to a
GPtrArray container that preserves the destroy notify functions.
GPtrArray's are faster than lists and provide more flexibility, so add
APIs to get hash keys and values using these containers too.
Given that we know the size at array initialization we can optimize the
allocation quite a bit, making it faster than the API using GList both at
creation time and for consumers.
GArray's g_array_append_val(), g_array_prened_val() and g_array_insert_val()
macros required an user to use literals to add a new value.
This could be inconvenient at times, but it's possible to avoid this with
recent compilers, in fact in case glib_typeof is defined we can take
advantage of it, to initialize a temporary variable to store the literal
value and pass its address to the actual function.
The array of offsets is little-endian, even on big-endian architectures
like s390x.
Fixes: ade71fb5 "gvariant: Don’t allow child elements to overlap with each other"
Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/2839
Signed-off-by: Simon McVittie <smcv@collabora.com>
posix_memalign() requires the alignment to be a multiple of sizeof(void*),
and a power of 2. Passing 8 does not fulfil both of those constraints on
Arm Morello which resulted in a "posix_memalign failed" test failure.
Co-authored-by: Graeme Jenkinson <graeme@capabilitieslimited.co.uk>
We may need to avoid using a cached temp directory for testing purposes,
so let's provide an internal API to perform such task.
This implies removing GOnce and going with mutex-based version, but
that's still using atomic logic in most unix implementations anyways.
This avoids needing to always serialise a variant before byteswapping it.
With variants in non-normal forms, serialisation can result in a large
increase in size of the variant, and a lot of allocations for leaf
`GVariant`s. This can lead to a denial of service attack.
Avoid that by changing byteswapping so that it happens on the tree form
of the variant if the input is in non-normal form. If the input is in
normal form (either serialised or in tree form), continue using the
existing code as byteswapping an already-serialised normal variant is
about 3× faster than byteswapping on the equivalent tree form.
The existing unit tests cover byteswapping well, but need some
adaptation so that they operate on tree form variants too.
I considered dropping the serialised byteswapping code and doing all
byteswapping on tree-form variants, as that would make maintenance
simpler (avoiding having two parallel implementations of byteswapping).
However, most inputs to `g_variant_byteswap()` are likely to be
serialised variants (coming from a byte array of input from some foreign
source) and most of them are going to be in normal form (as corruption
and malicious action are rare). So getting rid of the serialised
byteswapping code would impose quite a performance penalty on the common
case.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2797
If `g_variant_byteswap()` was called on a non-normal variant of a type
which doesn’t need byteswapping, it would return a non-normal output.
That contradicts the documentation, which says that the return value is
always in normal form.
Fix the code so it matches the documentation.
Includes a unit test.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2797
The entries in an offset table (which is used for variable sized arrays
and tuples containing variable sized members) are sized so that they can
address every byte in the overall variant.
The specification requires that for a variant to be in normal form, its
offset table entries must be the minimum width such that they can
address every byte in the variant.
That minimality requirement was not checked in
`g_variant_is_normal_form()`, leading to two different byte arrays being
interpreted as the normal form of a given variant tree. That kind of
confusion could potentially be exploited, and is certainly a bug.
Fix it by adding the necessary checks on offset table entry width, and
unit tests.
Spotted by William Manley.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2794
This improves a slow case in `g_variant_get_normal_form()` where
allocating many identical default values for the children of a
variable-sized array which has a malformed offset table would take a lot
of time.
The fix is to make all child values after the first invalid one be
references to the default value emitted for the first invalid one,
rather than identical new `GVariant`s.
In particular, this fixes a case where an attacker could create an array
of length L of very large tuples of size T each, corrupt the offset table
so they don’t have to specify the array content, and then induce
`g_variant_get_normal_form()` into allocating L×T default values from an
input which is significantly smaller than L×T in length.
A pre-existing workaround for this issue is for code to call
`g_variant_is_normal_form()` before calling
`g_variant_get_normal_form()`, and to skip the latter call if the former
returns false. This commit improves the behaviour in the case that
`g_variant_get_normal_form()` is called anyway.
This fix changes the time to run the `fuzz_variant_binary` test on the
testcase from oss-fuzz#19777 from >60s (before being terminated) with
2.3GB of memory usage and 580k page faults; to 32s, 8.3MB of memory
usage and 1500 page faults (as measured by `time -v`).
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2540
oss-fuzz#19777
This is equivalent to what `GVariantIter` does, but it means that
`g_variant_deep_copy()` is making its own `g_variant_get_child_value()`
calls.
This will be useful in an upcoming commit, where those child values will
be inspected a little more deeply.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2121
Building a `GVariant` using entirely random data may result in a
non-normally-formed `GVariant`. It’s always possible to read these
`GVariant`s, but the API might return default values for some or all of
their components.
In particular, this can easily happen when randomly generating the
offset tables for non-fixed-width container types.
If it does happen, bytewise comparison of the parsed `GVariant` with the
original bytes will not always match. So skip those checks.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2121