gvariant-core: Add a note about memory safety of children

When g_variant_get_child() is called on a variant which has not been
serialized, it serializes it which includes a call to
g_variant_release_children() and therefore means that any children
previously retrieved from the variant are no longer valid (unless
another reference is held on them) and consequently values borrowed from
those children are no longer safe to access. Add a note to the
g_variant_get_child_value() documentation to explain this.

Alternatively, we could say that after the child is freed, values
borrowed from it are no longer valid. But we already have an
implementation which hasn't changed in years which lets them stay valid
if the variant was serialized before the first
g_variant_get_child_value() call.

Here's a demonstration of the memory error:

static const char *get_first_child (GVariant *v) {
    g_autoptr(GVariant) child_v = g_variant_get_child_value (v, 0);
    return g_variant_get_string (child_v, NULL);
}
int main(int argc, char **argv) {
    g_autoptr(GVariant) v = g_variant_new("(@ss)", g_variant_new_string ("hello"), "world");
    const char *child1 = get_first_child (v);
    const char *child2;
    g_variant_get_child (v, 1, "&s", &child2);
    printf ("%s\n", child1); // this is a memory error
    return 0;
}
This commit is contained in:
Matthew Leeds 2020-04-27 16:41:14 -07:00
parent de8708cd95
commit 51b822787e

View File

@ -1026,6 +1026,12 @@ g_variant_n_children (GVariant *value)
* The returned value is never floating. You should free it with
* g_variant_unref() when you're done with it.
*
* Note that values borrowed from the returned child are not guaranteed to
* still be valid after the child is freed even if you still hold a reference
* to @value, if @value has not been serialised at the time this function is
* called. To avoid this, you can serialize @value by calling
* g_variant_get_data() and optionally ignoring the return value.
*
* There may be implementation specific restrictions on deeply nested values,
* which would result in the unit tuple being returned as the child value,
* instead of further nested children. #GVariant is guaranteed to handle