Mercury-Language/mercury

Segfault in only Mercury after solutions in a particular code path

Opened this issue · 3 comments

Sorry, although this seems to be a very consistent bug (crashes on Linux on 14.01, on DEV from last night, on a July 13 rotd, and on a rotd from a few days ago on OpenBSD), it's still very sensitive and my attempts to cut the code down have all suppressed the segfault.

Although there's some novel foreign_proc code in this project, none if it's reached before the segfault. There some semipure code involved in getting mutable variable data (that doesn't change for the lifetime of the program, so is promised to be pure).

Hopefully you can observe the segfault with the following commands:

$ git clone https://github.com/jrfondren/mmc-get.git
...
$ cd mmc-get
$ git checkout a2e15d837a2423172212703c204c38601dd9db29
...
HEAD is now at a2e15d8 get a segfaulting commit hash
$ mmc --make mmcget
...
$ ./mmcget update
...
$ ./mmcget get mmc-get

*** Mercury runtime: caught segmentation violation ***
cause: address not mapped to object
address involved: (nil)
This may have been caused by a stack overflow, due to unbounded recursion.
exiting from signal handler
Segmentation fault (core dumped)

You'll get a warning that's incidentally from the affected code (fixing it to avoid the warning doesn't fix the problem):

    else if
        Args = ["get", Want],
        solutions(want(Want), [R @ unreviewed(Package)])
    then
        write_package(R, !IO),  % segfaults
        ask_to_clone(R, !IO)
        %write_package(unreviewed(Package), !IO),
        %ask_to_clone(unreviewed(Package), !IO)

The non-segfaulting version, commented here, still sometimes segfaults. It's much less reliable than this version.

some relevant code:

% in mmcget.m
:- pred want(string::in, reviewed::out) is nondet.
want(Substr, R) :-
    ( packages(P), R = reviewed(P) ; unreviewed(P), R = unreviewed(P) ),
    sub_string_search(to_lower(P^name), to_lower(Substr), _).
...
% in manager.m
:- pred packages(package::out) is nondet.
:- pred unreviewed(package::out) is nondet.

:- mutable(reviewed, list(package), [], ground, [untrailed, attach_to_io_state]).
:- mutable(unreviewed, list(package), [], ground, [untrailed, attach_to_io_state]).
:- initialize init_packages/2.

:- pragma promise_pure packages/1.
packages(P) :-
    semipure get_reviewed(Ps),
    list.member(P, Ps).

:- pragma promise_pure unreviewed/1.
unreviewed(P) :-
    semipure get_unreviewed(Ps),
    list.member(P, Ps).

where init_packages supplies the mutable variables from the files you get with 'mmcget update'.

other 'get' commands work fine:

$ ./mmcget get getr
Cloning into 'getr'...
remote: Enumerating objects: 14, done.
remote: Counting objects: 100% (14/14), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 24 (delta 4), reused 12 (delta 4), pack-reused 10
Unpacking objects: 100% (24/24), done.

the distinction seems to be that mmc-get is the only target that's in unreviewed.list, which on a Linux system you'll find in $HOME/.config/mmc-get/ after doing the update. Copying targets that work into unreviewed and changing the name to make them distinctive results in new targets that segfault. If they're not distinctive, the same data can be worked with just fine:

$ tail -1 ~/.config/mmc-get/reviewed.list
package("PaulBone/protobuf-mercury", other, git, head, "https://github.com/PaulBone/protobuf-mercury.git", ["serializing", "wrapper", "nonauthor", "special_license"], [], [], [], [c], "Protocol buffers", "").
$ tail -1 ~/.config/mmc-get/reviewed.list >> ~/.config/mmc-get/unreviewed.list 
$ ./mmcget get protobuf
[1] 
Name             : PaulBone/protobuf-mercury
URL              : https://github.com/PaulBone/protobuf-mercury.git
Tags             : serializing wrapper nonauthor special_license
License          : other
Builds           : 
Dependencies     : []
Version Control  : git (releases: head)
FFI              : [c]
Description      : Protocol buffers
[2] 
Name (UNREVIEWED)? PaulBone/protobuf-mercury
URL              ? https://github.com/PaulBone/protobuf-mercury.git
Tags             ? serializing wrapper nonauthor special_license
License          ? other
Builds           ? 
Dependencies     ? []
Version Control  : git (releases: head)
FFI              ? [c]
Description      ? Protocol buffers

Ambiguous result. Your choice? [1..2, show, brief, q] 2

Name (UNREVIEWED)? PaulBone/protobuf-mercury
URL              ? https://github.com/PaulBone/protobuf-mercury.git
Tags             ? serializing wrapper nonauthor special_license
License          ? other
Builds           ? 
Dependencies     ? []
Version Control  : git (releases: head)
FFI              ? [c]
Description      ? Protocol buffers
Really get unreviewed package? [y/n] y
Cloning into 'PaulBone/protobuf-mercury'...
remote: Enumerating objects: 412, done.
remote: Total 412 (delta 0), reused 0 (delta 0), pack-reused 412
Receiving objects: 100% (412/412), 86.57 KiB | 428.00 KiB/s, done.
Resolving deltas: 100% (180/180), done.

$ tail -1 ~/.config/mmc-get/reviewed.list | perl -pe 's/PaulBone/xPaulBone/' >> ~/.config/mmc-get/unreviewed.list 
$ ./mmcget get xPaulBone/protobuf-mercury

*** Mercury runtime: caught segmentation violation ***
cause: address not mapped to object
address involved: (nil)
This may have been caused by a stack overflow, due to unbounded recursion.
exiting from signal handler
Segmentation fault (core dumped)
wangp commented

Attached a reduced test case (Github doesn't allow .m file extension).
bug72.m.txt

I have diagnosed the actual problem in a post on mercury-developers.