After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 682698 - const string[] not NULL terminated; crashes in GBoxed property getter
const string[] not NULL terminated; crashes in GBoxed property getter
Status: RESOLVED OBSOLETE
Product: vala
Classification: Core
Component: Arrays
unspecified
Other All
: Normal major
: ---
Assigned To: Vala maintainers
Vala maintainers
Depends on:
Blocks:
 
 
Reported: 2012-08-25 21:59 UTC by Philip Withnall
Modified: 2018-05-22 14:29 UTC
See Also:
GNOME target: ---
GNOME version: ---



Description Philip Withnall 2012-08-25 21:59:15 UTC
If I have a class with a const string[] which is returned in a property getter, Vala will generate C code which doesn’t NULL-terminate the array before calling g_value_set_boxed() on it. Since the array is handled as a G_TYPE_STRV boxed type, the boxing function assumes it’ll be NULL-terminated, and ends up walking off into random memory.


This Vala:


public class MyClass {
  private const string[] _foobar = {
    "baz"
  };

  public string[] foobar {
    get { return this._foobar; }
  }
}


generates something like the following C (trimmed down from an example in libfolks):


static const gchar* MY_CLASS__foobar[1] = {"baz"};
…
g_object_class_install_property (G_OBJECT_CLASS (klass), MY_CLASS_FOOBAR, g_param_spec_boxed ("foobar", "foobar", "foobar", G_TYPE_STRV, G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB | G_PARAM_READABLE));
…
static void _vala_my_class_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) {
	…
	switch (property_id) {
		case MY_CLASS_FOOBAR:
		{
			int length;
			g_value_set_boxed (value, my_class_get_foobar ((MyClass*) self, &length));
		}
		break;
	}
}
…
static gchar** my_class_get_foobar (MyClass* base, int* result_length1) {
	…
	_tmp0_ = MY_CLASS__foobar;
	_tmp0__length1 = G_N_ELEMENTS (MY_CLASS__foobar);
	if (result_length1) {
		*result_length1 = _tmp0__length1;
	}
	result = _tmp0_;
	return result;
}
Comment 1 Philip Withnall 2012-08-25 21:59:35 UTC
This is with git master, 049a77952370fcad5f6fbdab08dd84d89f40151a.
Comment 2 Philip Withnall 2013-10-08 13:36:06 UTC
Still a problem with 75359c3cab2e979d087ce0a19735bf86add33b42.
Comment 3 Luca Bruno 2013-10-08 13:45:24 UTC
We can't auto-null terminate arrays before calling methods. What we can do is null-terminating the constant arrays declared in vala.
Comment 4 Philip Withnall 2013-10-08 20:07:20 UTC
(In reply to comment #3)
> What we can do is
> null-terminating the constant arrays declared in vala.

That’s what I was suggesting. :-)
Comment 5 Luca Bruno 2013-10-12 10:07:34 UTC
Unfortunately, I don't think we can add a NULL to the constant. Adding it will increase G_N_ELEMENTS, and that would break applications that expose such constants in their public API.
Comment 6 Philip Withnall 2013-10-14 11:14:57 UTC
(In reply to comment #5)
> Unfortunately, I don't think we can add a NULL to the constant. Adding it will
> increase G_N_ELEMENTS, and that would break applications that expose such
> constants in their public API.

You could change the code which generates G_N_ELEMENTS() calls to use G_N_ELEMENTS()-1 instead; or something similar.
Comment 7 Luca Bruno 2013-10-14 11:16:43 UTC
Still breaks C usage and existing vala applications for public constants, it's not doable.
Perhaps you better var copy = your_array; and use take_boxed.
Comment 8 Philip Withnall 2013-10-22 07:31:15 UTC
(In reply to comment #7)
> Perhaps you better var copy = your_array; and use take_boxed.

Why can’t Vala do that automatically? Instead of generating the following code in my_class_get_property():

	case TPF_PERSONA_LINKABLE_PROPERTIES:
	{
		int length;
		g_value_set_boxed (value, folks_persona_get_linkable_properties ((FolksPersona*) self, &length));
	}

it could generate this:

	case TPF_PERSONA_LINKABLE_PROPERTIES:
	{
		gchar **normal_array;
		GStrv *null_terminated_array;
		int length;
		normal_array = folks_persona_get_linkable_properties ((FolksPersona*) self, &length);
		null_terminated_array = _vala_array_dup1 (normal_array, length);
		g_value_take_boxed (value, null_terminated_array);
	}

In any case it needs to do something — whether by disallowing const arrays to be returned from property getters, or (preferably) outputting the code above. At the moment the code Vala generates is broken, and the programmer is given no warning of this.
Comment 9 Luca Bruno 2013-10-22 08:21:14 UTC
1) There's no reason to disallow returning const arrays from properties, you are only seeing your own problem here and forgetting that arrays can be non-null terminated only because you're using a function that expects a null terminated array.
2) Vala can't (and won't because we try to avoid subtle copies of arrays) determine if an array is null terminated because of the nature of C.

This would be a WONTFIX, but I don't close as maybe somebody has a good solution.
Comment 10 Philip Withnall 2013-10-22 09:32:55 UTC
(In reply to comment #9)
> 1) There's no reason to disallow returning const arrays from properties, you
> are only seeing your own problem here and forgetting that arrays can be
> non-null terminated only because you're using a function that expects a null
> terminated array.

String arrays (of type GStrv) are defined by GLib to be NULL-terminated, so if Vala chooses to generate GStrv code for a string[] property, it should ensure that values returned by that property’s getter are NULL-terminated — either by adding the NULL terminator, or by throwing a compiler error if the programmer tries to return a non-NULL-terminated string array.

> 2) Vala can't (and won't because we try to avoid subtle copies of arrays)
> determine if an array is null terminated because of the nature of C.

You can’t avoid copying the array: g_value_set_boxed() always takes a copy. By using the g_value_take_boxed() suggestion in comment #8 you’re simply moving the copy into the generated C code, rather than doing it inside GLib.

> This would be a WONTFIX, but I don't close as maybe somebody has a good
> solution.

Will my suggestion from comment #8 not work? :-)
Comment 11 Luca Bruno 2013-10-22 09:48:57 UTC
(In reply to comment #10)
> String arrays (of type GStrv) are defined by GLib to be NULL-terminated, so if
> Vala chooses to generate GStrv code for a string[] property, it should ensure
> that values returned by that property’s getter are NULL-terminated — either by
> adding the NULL terminator, or by throwing a compiler error if the programmer
> tries to return a non-NULL-terminated string array.

True that, we can forbid returning const arrays defined in vala from within properties. Then you have to handle that by yourself.

> You can’t avoid copying the array: g_value_set_boxed() always takes a copy. By
> using the g_value_take_boxed() suggestion in comment #8 you’re simply moving
> the copy into the generated C code, rather than doing it inside GLib.
> 
> Will my suggestion from comment #8 not work? :-)

Yes, use take_boxed manually :-) As said, vala can't decide that automagically.
Comment 12 Luca Bruno 2013-10-22 09:50:22 UTC
By take_boxed manually I mean having an owned getter:

public string[] foobar {
    owned get { return this._foobar; }
}

Maybe something like this, or owned property. Using owned will create a copy for you.
Comment 13 Philip Withnall 2013-10-22 10:18:15 UTC
(In reply to comment #11)
> (In reply to comment #10)
> > Will my suggestion from comment #8 not work? :-)
> 
> Yes, use take_boxed manually :-) As said, vala can't decide that automagically.

Why can’t Vala decide that automagically? It knows that the array is const and not NULL-terminated, and it knows that the getter needs to return a NULL-terminated array. What else does it need to know?

(In reply to comment #12)
> public string[] foobar {
>     owned get { return this._foobar; }
> }
> 
> Maybe something like this, or owned property. Using owned will create a copy
> for you.

Changing it to be an owned getter would break folks’ API, so we can’t do that.
Comment 14 Luca Bruno 2013-10-22 10:20:54 UTC
(In reply to comment #13)
> Why can’t Vala decide that automagically? It knows that the array is const and
> not NULL-terminated, and it knows that the getter needs to return a
> NULL-terminated array. What else does it need to know?

Wouldn't doing a copy with an unowned getter leak?

> Changing it to be an owned getter would break folks’ API, so we can’t do that.

Keep a copy of the array somewhere and return it.
Comment 15 Philip Withnall 2013-10-22 11:26:39 UTC
(In reply to comment #14)
> (In reply to comment #13)
> > Why can’t Vala decide that automagically? It knows that the array is const and
> > not NULL-terminated, and it knows that the getter needs to return a
> > NULL-terminated array. What else does it need to know?
> 
> Wouldn't doing a copy with an unowned getter leak?

Not if you pass it to g_value_take_boxed() instead of g_value_set_boxed().

> > Changing it to be an owned getter would break folks’ API, so we can’t do that.
> 
> Keep a copy of the array somewhere and return it.

That would work, but yeuch.
Comment 16 Luca Bruno 2013-10-22 13:07:26 UTC
(In reply to comment #15)
> Not if you pass it to g_value_take_boxed() instead of g_value_set_boxed().

When is it freed then?
Comment 17 Philip Withnall 2013-10-22 13:17:52 UTC
(In reply to comment #16)
> (In reply to comment #15)
> > Not if you pass it to g_value_take_boxed() instead of g_value_set_boxed().
> 
> When is it freed then?

When the GValue is unset, same as usual. Take a look at the code for g_value_[take|set]_boxed() in GLib’s gboxed.c. The only difference between them is that g_value_set_boxed() calls g_boxed_copy() on the data it’s passed. For a string array this equates to g_strdupv().
Comment 18 Luca Bruno 2013-10-22 21:09:54 UTC
I have mixed two problems here, my bad: the getter and the get_property. In get_property, yes, it's of course possible to copy the array manually instead of letting set_boxed doing it.
The other problem is the getter: there you must return an unowned reference and vala can't do anything about that, other than warning about returning a non-null terminated array.
If we fix get_property, it means we'll have two different behaviors though.
Comment 19 Philip Withnall 2013-10-22 21:34:35 UTC
(In reply to comment #18)
> The other problem is the getter: there you must return an unowned reference and
> vala can't do anything about that, other than warning about returning a
> non-null terminated array.

The getter has an integer out parameter which returns the length.
e.g.:

gchar** folks_persona_get_linkable_properties (FolksPersona* self, int* result_length1);
Comment 20 Luca Bruno 2013-10-22 21:59:59 UTC
It's ok then, it should be safe to do the copy in get_property.
Comment 21 GNOME Infrastructure Team 2018-05-22 14:29:01 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to GNOME's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/vala/issues/316.