Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gobject Model Crash (invalid access to freed memory) #174

Open
bvaugon opened this issue Dec 14, 2023 · 1 comment
Open

Gobject Model Crash (invalid access to freed memory) #174

bvaugon opened this issue Dec 14, 2023 · 1 comment

Comments

@bvaugon
Copy link

bvaugon commented Dec 14, 2023

At least from OCaml 5.1.1, lablgtk3 performs invalid accesses to freed memory blocks.

Example :

let column_list = new GTree.column_list in
let column = column_list#add Gobject.Data.string in
let model = GTree.list_store column_list in
for i = 1 to 200 do
  let row = model#append () in
  model#set ~row ~column ("Value " ^ string_of_int i);
done;
match model#get_iter_first with
| None -> ()
| Some row ->
  let rec loop i =
    let str = model#get ~row ~column in
    Printf.printf "model[%d] = %S\n%!" i str;
    if model#iter_next row then loop (i + 1) in
  loop 0

To compile with:

ocamlopt -I +../lablgtk3 -I +unix unix.cmxa lablgtk3.cmxa bug.ml -o bug

If you run it, you will periodically see (at each minor GC run) some random data like:

[...]
model[147] = "Value 148"
model[148] = "Value 149"
model[149] = "\161$\021\251\197U\000\0000"
model[150] = "Value 151"
model[151] = "Value 152"
[...]

Explanation :

  • model#get calls Gobject.Data.of_value
  • Gobject.Data.of_value calls Gobject.Value.get_conv
  • Gobject.Value.get_conv calls Gobject.Value.get
  • Gobject.Value.get is the C function ml_g_value_get_mlvariant
  • ml_g_value_get_mlvariant calls g_value_get_mlvariant
  • g_value_get_mlvariant executes the G_TYPE_STRING case
  • copy_string from the G_TYPE_STRING case sometimes calls the GC
  • the GC frees the custom block containing the GValue
  • the finalizer of the custom block frees the GValue
  • the C string is already freed when copied by copy_string

This behavior may be finely observed defining environment variable OCAMLRUNPARAM=v=0xFFF and adding some traces in the finalizer (ml_final_gboxed) and in the g_value_get_mlvariant function. You will see that the GValue is effectively freed before its copy by copy_string.

The fundamental problem is that, even if the OCaml variable v is still in the lexical scope of Gobject.Data.of_value when Gobject.Data.of_value calls Gobject.Value.get_conv, it seems to be removed from GC roots (the stack in this case) by the compiler, and is then freed too early.

I don't know if this behavior of the OCaml compiler, i.e. to allow to free blocks that are lexically scoped but no longer accessed, is a well designed and well documented property of OCaml, but if it is not considered a bug by the OCaml team, I suggest to modify lablgtk3, and to register custom blocks as GC roots (using a CAMLlocalX macro) in all lablgtk3 primitives like ml_g_value_get_mlvariant.

Best regards,
Benoît.

@bvaugon
Copy link
Author

bvaugon commented Dec 15, 2023

In fact, this bug seems to be quite old and do not need to use OCaml 5.1.1. It is just more difficult to observe using older version of OCaml since reading freed blocks was more rarely dangerous and small freed blocks kept their contents a longer time.

To observe this bug using OCaml 4.14.1, for example, use larger strings. Simply replace the call to model#set by:
model#set ~row ~column ("Value " ^ String.make 1_000_000 'x' ^ string_of_int i);
and replace the print by:
assert (str = ("Value " ^ String.make 1_000_000 'x' ^ string_of_int (i + 1)));
and the program segfaults (or raises an assertion failure) just after the first GC run.

Moreover, traces in the finalizer (ml_final_gboxed) and in g_value_get_mlvariant shows that the GValue is finalized before the end of its copy (I mean after getting its length but before copying its contents), as using OCaml 5.1.1.

We can observe a similar problem using OCaml 4.08.1. I didn't go futher in the History.

@bvaugon bvaugon changed the title Crash (invalid access to freed memory) using OCaml 5.1.1 Gobject Model Crash (invalid access to freed memory) Dec 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant