Corrupted memory when modifying a struct passed as argument
Vala code:
struct Test {
string s;
}
void modify(Test t) {
t.s = "b";
}
void main() {
Test t = { "a" };
modify(t);
stdout.printf("t.s: %s.\n", t.s);
}
Generated C code:
void
modify (Test* t)
{
gchar* _tmp0_;
g_return_if_fail (t != NULL);
_tmp0_ = g_strdup ("b");
_g_free0 ((*t).s);
(*t).s = _tmp0_;
}
void
_vala_main (void)
{
Test t = {0};
gchar* _tmp0_;
Test _tmp1_ = {0};
Test _tmp2_;
FILE* _tmp3_;
Test _tmp4_;
const gchar* _tmp5_;
_tmp0_ = g_strdup ("a");
_g_free0 (_tmp1_.s);
_tmp1_.s = _tmp0_;
t = _tmp1_;
_tmp2_ = t;
modify (&_tmp2_);
_tmp3_ = stdout;
_tmp4_ = t;
_tmp5_ = _tmp4_.s;
fprintf (_tmp3_, "t.s: %s.\n", _tmp5_);
test_destroy (&t);
}
Output:
t.s: .
The struct is copied bit by bit to a _tmp2_
variable, whose address is then passed to modify
. This function later frees the string and assigns a new one. When modify
returns, _tmp2_
eventually goes out of scope and the new b
string is never freed. But the a
string that is still present in the t
object is freed by the modify
function, so the printf
prints garbage and test_destroy
will later result in a double-free.
Per my understanding structs are always be passed by value, so annotating the Test t
argument in the modify
method unowned
/owned
should not make any difference (just like unowned
/owned
doesn't make any sense for an int
). But for some reason putting an owned
in front of the Test t
argument results in valid C code.