Skip to content

Segfault involving recursive lazy values and GC shortcutting #13930

Description

@lthls

The following program segfaults:

let f x =
  let rec l =
    let v = lazy x in
    Gc.minor ();
    v
  in
  l
let lazy_one = f 1

What happens is that lazy x is compiled as (makeforward x), as f could be called with float or lazy arguments (otherwise we would get x directly).
This lures Value_rec_check and Value_rec_compiler into thinking that l will always be a block of size 1 with tag Forward_tag.
However, if a GC runs between the moment the value is allocated and the moment it is used to update the dummy value used for compiling recursive bindings, then if the value turns out to be neither a float nor a lazy the GC will shortcut it, and then we will try to update the dummy block using the integer 1.

I don't have a good idea of how to fix that. I think that we could update the environments used in Value_rec_check and Value_rec_compiler to either not consider forward blocks as having a static size (easier to do but may introduce regressions), or to clear the static sizes of forward blocks whenever a non-trivial expression is encountered.

As I said in #13919, I also think that we could completely remove the lazy optimisations from this part of the compiler, making Value_rec_check and Value_rec_compiler simpler, and instead implement them in the backends (or in Simplif, if we don't want to duplicate the implementation).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions