As noted in #12466 (comment), the bind function in Cmm_helpers doesn't properly protect against reads of mutable variables. It took me a while to find a way to exploit that, but I finally got it using method calls.
Here is an example:
let () =
let o1 = object val x = 0 method get () = x end in
let o2 = object method get () = 1 end in
let r = ref o1 in
let n = (!r)#get (r := o2) in
print_int n; print_newline ()
The assignment will be evaluated between a piece of code that gets the actual method, and the piece that calls it, the the method gets an object different from the one it is expecting. In this particular example, we retrieve the method get from o1, pass it o2, and then we read whatever garbage happens to be stored where x would be if o2 had the same layout as o1.
Note that the issue is very unlikely to come up in practice. First, it requires the Closure middle-end (Flambda enforces a proper evaluation order before going to Cmm). In addition, the method call has to be at toplevel (so that it doesn't use the cached method call version, which only has one occurrence of the object under the bind). And of course a local reference has to be involved, which combined with the toplevel requirement and Closure's compilation strategy for toplevel bindings, means that this can only be inside the definition of a non-function toplevel binding. Finally, the reference holding the object has to be modified in one of the arguments to the function call.
As noted in #12466 (comment), the
bindfunction inCmm_helpersdoesn't properly protect against reads of mutable variables. It took me a while to find a way to exploit that, but I finally got it using method calls.Here is an example:
The assignment will be evaluated between a piece of code that gets the actual method, and the piece that calls it, the the method gets an object different from the one it is expecting. In this particular example, we retrieve the method
getfromo1, pass ito2, and then we read whatever garbage happens to be stored wherexwould be ifo2had the same layout aso1.Note that the issue is very unlikely to come up in practice. First, it requires the Closure middle-end (Flambda enforces a proper evaluation order before going to Cmm). In addition, the method call has to be at toplevel (so that it doesn't use the cached method call version, which only has one occurrence of the object under the
bind). And of course a local reference has to be involved, which combined with the toplevel requirement and Closure's compilation strategy for toplevel bindings, means that this can only be inside the definition of a non-function toplevel binding. Finally, the reference holding the object has to be modified in one of the arguments to the function call.