This was erroring on recent GCC because `struct heap_dict` is smaller than
the publicly provided size (guintptr[16]) in the header for GVariantDict.
Port to use `g_malloc()` directly, and use a static assertion to ensure
we’re allocating the larger of the two struct sizes.
For CHERI-enabled architectures such as Arm Morello we have to align the
GVariantIter to pointer alignment (16 bytes for Morello) but defining the
opaque struct as 16 gsize elements only aligns to 8 bytes.
This does not change the layout for existing architecutre since there
gsize is the same as guintptr.
Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842
Use guinptr instead if gsize. While this increases the structure size
for Morello, it is still only 256 bytes which should not matter for a
type where we expect few instances to exist at any given time.
This does not increase the size for existing architectures since gsize
is the same as guintptr for all of them.
Helps: https://gitlab.gnome.org/GNOME/glib/-/issues/2842
As with commit 9ae59bd647, deeply nested
maybes in an array can be exploited by a malicious caller to cause a
geometric increase in processing time and number of `GVariant` instances
handled by the `g_variant_print()` code.
Optimise this by skipping recursing through most of the chain of maybes,
thus avoiding all the setup checks in each recursive call.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
oss-fuzz#54577
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>
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
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
The past few commits introduced the concept of known-good offsets in the
offset table (which is used for variable-width arrays and tuples).
Good offsets are ones which are non-overlapping with all the previous
offsets in the table.
If a bad offset is encountered when indexing into the array or tuple,
the cached known-good offset index will not be increased. In this way,
all child variants at and beyond the first bad offset can be returned as
default values rather than dereferencing potentially invalid data.
In this case, there was no information about the fact that the indexes
between the highest known-good index and the requested one had been
checked already. That could lead to a pathological case where an offset
table with an invalid first offset is repeatedly checked in full when
trying to access higher-indexed children.
Avoid that by storing the index of the highest checked offset in the
table, as well as the index of the highest good/ordered offset.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2121
This is similar to the earlier commit which prevents child elements of a
variable-sized array from overlapping each other, but this time for
tuples. It is based heavily on ideas by William Manley.
Tuples are slightly different from variable-sized arrays in that they
contain a mixture of fixed and variable sized elements. All but one of
the variable sized elements have an entry in the frame offsets table.
This means that if we were to just check the ordering of the frame
offsets table, the variable sized elements could still overlap
interleaving fixed sized elements, which would be bad.
Therefore we have to check the elements rather than the frame offsets.
The logic of checking the elements up to the index currently being
requested, and caching the result in `ordered_offsets_up_to`, means that
the algorithmic cost implications are the same for this commit as for
variable-sized arrays: an O(N) cost for these checks is amortised out
over N accesses to O(1) per access.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: #2121
The following few commits will add a couple of new fields to
`GVariantSerialised`, and they should be zero-filled by default.
Try and pre-empt that a bit by zero-filling `GVariantSerialised` by
default in a few places.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #2121
scan-build can’t link the types used in `g_variant_builder_init()` with
the (same) types used in `g_variant_builder_end()`, so ends up assuming
that the children have not been initialised.
At runtime, this is prevented by the precondition checks on
`GVSB()->offset` in `g_variant_builder_end()`. scan-build doesn’t notice
that though.
Avoid a scan-build warning by zero-initialising the children array when
running static analysis. Doing this unconditionally would be an
unnecessary performance hit.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Add SPDX license (but not copyright) headers to all files which follow a
certain pattern in their existing non-machine-readable header comment.
This commit was entirely generated using the command:
```
git ls-files glib/*.[ch] | xargs perl -0777 -pi -e 's/\n \*\n \* This library is free software; you can redistribute it and\/or\n \* modify it under the terms of the GNU Lesser General Public/\n \*\n \* SPDX-License-Identifier: LGPL-2.1-or-later\n \*\n \* This library is free software; you can redistribute it and\/or\n \* modify it under the terms of the GNU Lesser General Public/igs'
```
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #1415
This will help static analysers, similarly to with the previous commit.
This introduces no functional changes.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Replace all instances of ensure_valid_builder or ensure_valid_dict
calls used as arguments to the g_return_if_fail or
g_return_value_if_fail macros with otherwise equivalent code that
always calls these functions, thus ensuring their side effects always
apply. This commit does not change any external or private interfaces.
Fixes#2612
Convert all the call sites which use `g_memdup()`’s length argument
trivially (for example, by passing a `sizeof()` or an existing `gsize`
variable), so that they use `g_memdup2()` instead.
In almost all of these cases the use of `g_memdup()` would not have
caused problems, but it will soon be deprecated, so best port away from
it
In particular, this fixes an overflow within `g_bytes_new()`, identified
as GHSL-2021-045 by GHSL team member Kevin Backhouse.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Fixes: GHSL-2021-045
Helps: #2319
This doesn’t change the behaviour of the code, but should squash some
compiler/static analysis warnings about ‘are you sure you got the
precedence right here?’.
Coverity CID: #1159470
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
The implementation didn’t match the documentation. The implementation
has the right behaviour (wrt not allowing embedded nuls, validating
UTF-8, and returning a default value if an invalid string is detected),
so keep that and fix the documentation to match.
The [`GVariant`
specification](https://people.gnome.org/~desrt/gvariant-serialisation.pdf)
is incorrect on this point, and the implementation of GLib was
purposefully changed after the specification was published (but before
`GVariant` became API-stable in GLib). The behaviour in GLib
(specifically concerning all strings being in UTF-8) is consistent with
D-Bus.
Spotted by William Manley.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
This was mostly machine generated with the following command:
```
codespell \
--builtin clear,rare,usage \
--skip './po/*' --skip './.git/*' --skip './NEWS*' \
--write-changes .
```
using the latest git version of `codespell` as per [these
instructions](https://github.com/codespell-project/codespell#user-content-updating).
Then I manually checked each change using `git add -p`, made a few
manual fixups and dropped a load of incorrect changes.
There are still some outdated or loaded terms used in GLib, mostly to do
with git branch terminology. They will need to be changed later as part
of a wider migration of git terminology.
If I’ve missed anything, please file an issue!
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Even if g_variant_get_va(), which we eventually call, checks on the
validity of the value argument, check it early to avoid surprises, such
as this intermittent FreeBSD test failure:
(/var/tmp/gitlab_runner/builds/3fe11159/0/GNOME/glib/_build/gio/tests/gdbus-connection:65788): GLib-CRITICAL **: 15:13:25.670: g_variant_get_va: assertion 'value != NULL' failed
Similar to 3837b83f, glibc memcmp is declared with the first two
arguments annotated as non-null via an attribute, which results in the
undefined behaviour sanitizer considering it to be UB to pass a null
pointer there (even if we are comparing 0 bytes, and hence not actually
dereferencing the pointer).
This shows up in /gvariant/serialiser/children when run with the
undefined behaviour sanitizer.
Signed-off-by: Simon McVittie <smcv@collabora.com>
glib/gvariant.c: In function ‘g_variant_new_strv’:
glib/gvariant.c:1563:17: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gssize’ {aka ‘long int’} [-Werror=sign-compare]
for (i = 0; i < length; i++)
^
glib/gvariant.c: In function ‘g_variant_new_objv’:
glib/gvariant.c:1699:17: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gssize’ {aka ‘long int’} [-Werror=sign-compare]
for (i = 0; i < length; i++)
^
glib/gvariant.c: In function ‘g_variant_new_bytestring_array’:
glib/gvariant.c:1939:17: error: comparison of integer expressions of different signedness: ‘gsize’ {aka ‘long unsigned int’} and ‘gssize’ {aka ‘long int’} [-Werror=sign-compare]
for (i = 0; i < length; i++)
^
Otherwise the GVariant would later fail internal alignment checks,
aborting the program.
If unaligned data is provided to (for example)
g_variant_new_from_data(), it will copy the data into a new aligned
allocation. This is slow, but better than crashing. If callers want
better performance, they should provide aligned data in their call, and
it will not be copied or reallocated.
Includes a unit test.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
https://gitlab.gnome.org/GNOME/glib/issues/1342
That should format these character classes correctly in the
documentation, and prevent them being interpreted by gtk-doc as (broken)
Markdown hyperlinks.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
This fits better with the convention in the rest of GLib where arbitrary
8-bit values are represented as guint8, avoiding the potential confusing
of a name which references ‘char’s.
This is not an API break, as both guint8 and guchar are unconditionally
typedeffed to unsigned char.
https://gitlab.gnome.org/GNOME/glib/issues/896