Consider the following function:
let example x = if true then "yay" else "nay"
Is there a way to analyse the function’s closure, so that I can somehow access the if-then-else expression inside of the function and get the two “else-then” sub expressions? Or maybe change the condition from true to false?
Imagine I cannot modify this function. This is always going to return “yay” no matter the input. Is there a way that I can access “nay” by looking at the function’s closure? Or maybe changing the condition from true to false, so that it evaluates to the else statement? Can this be done only using the Obj module?
Thanks in advance.
The short answer is “no”: simply speaking, the closure does not contain the expression themselves but the compiled machine code of the body of the function. There is no “object” that could be retrieved corresponding to one of the branches of the condition.
Perhaps the request would be clearer if you explained what you are actually trying to achieve.
I’m trying to do a programming challenge in my university. We were given a similar function, that has a “secret message” that is only visible when evaluating the function in a separate (private/unaccessible) interpreter. However, the else branch is not accessible, because the condition always evaluates to true.
Our objective is to, somehow figure out what the message in the else branch is using the Obj library, or maybe change the condition to “false” so that it evaluates the else branch.
If the compiler is smart enough, it will reduce
if true then a else b to
a, and will lost
b. Such an optimisation is quite trivial (far more than an efficient register allocations)
But the expression is inside of a function. Doesn’t the compiler need to evaluate the argument first so that we can recover the expression from the inside?
No inlining and constant propagation can be done inside bodies of function.
Typically, the native compiler (with or without flambda) optimizes your function to
let f _ = "yay"
If the actual code is less trivial, it is possible to have a condition that always evaluates to true but that the compiler cannot predict.
You can construct an artificial example like this:
let example x = if Sys.opaque_identity true then "yay" else "nay"
This will still not allow you to recover the
"nay" payload without a low-level debugger though, as constant strings are never stored in closures.
However, here is a case where it may be possible:
let secret_payload = Sys.opaque_identity "nay"
let example x = if Sys.opaque_identity true then "yay" else secret_payload
If the compiler hasn’t done too much optimisation, then the
secret_payload variable will be part of
example’s closure and can be retrieved by inspecting the closure block (by trial and error, there is no guaranteed index at which it would be stored).
My colleague @zozozo has written a library, memgraph, which should help you a bit understanding the representation of OCaml values (including closures) in memory.