SEGV in tabled deterministic predicate, but only when compiled

Version 10.1.2

This only fails when compiled:

ERROR: \[Thread main\] Deterministic procedure stats:resourceStats/1 failed
ERROR: \[Thread main\] In:
ERROR: \[Thread main\]   \[20\] throw(error(determinism_error(…,det,fail,property),context(…,*260)))
ERROR: \[Thread main\]   \[16\] main*(‘<garbage_collected>’) at /home/alanbur/prolog/p2k/p2k:105
ERROR: \[Thread main\]   \[15\] catch(user:main\_(…),\_322,user:handleError(\_344)) at /opt/swipl-10.1.2/lib/swipl/boot/init.pl:565
ERROR: \[Thread main\]   \[14\] catch_with_backtrace(‘<garbage_collected>’,‘<garbage_collected>’,‘<garbage_collected>’) at /opt/swipl-10.1.2/lib/swipl/boot/init.pl:645
ERROR: \[Thread main\]
ERROR: \[Thread main\] Note: some frames are missing due to last-call optimization.
ERROR: \[Thread main\] Re-run your program in debug mode (:- debug.) to get more detail.

Then after some backtracking where there shouldn’t be any:

ERROR: Received fatal signal 11 (segv)
Time: Wed Feb 11 15:21:14 2026
Inferences: 85
Thread: 2 (gc)
C-stack trace labeled "crash":
  [0] save_backtrace() at /data2/tmp/swipl-devel-10.1.2/src/os/pl-cstack.c:334 [0x7fb184dc8bf8]
..  [1] sigCrashHandler() at /data2/tmp/swipl-devel-10.1.2/src/os/pl-cstack.c:845 [0x7fb184d5df53]
..  [2] __restore_rt() at libc_sigaction.c:? [0x7fb184ae0df0]
..  [3] unallocClauseIndexTableEntries() at /data2/tmp/swipl-devel-10.1.2/src/pl-index.c:942 [0x7fb184dbf7f1]
...  [4] freeHeap() at /data2/tmp/swipl-devel-10.1.2/src/pl-alloc.c:214 [0x7fb184dbdb4c]
  [5] cleanDefinition() at /data2/tmp/swipl-devel-10.1.2/src/pl-proc.c:1882 [0x7fb184daf2cf]
  [6] PL_next_solution_guarded___LD() at /data2/tmp/swipl-devel-10.1.2/src/pl-vmi.c:4359 [0x7fb184cc7f64]
  [7] PL_next_solution___LD() at /data2/tmp/swipl-devel-10.1.2/src/pl-wam.c:3571 [0x7fb184d679b6]
  [8] PL_call_predicate() at /data2/tmp/swipl-devel-10.1.2/src/pl-fli.c:4533 [0x7fb184d9b774]
  [9] GCmain() at /data2/tmp/swipl-devel-10.1.2/src/pl-thread.c:6986 [0x7fb184d166cc]
  [10] start_thread() at ./nptl/./nptl/pthread_create.c:448 [0x7fb184b33b7b]
.  [11] __GI___clone3() at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:80 [0x7fb184bb17b8]


PROLOG STACK (without arguments):
  [4] garbage_collect_clauses/0 [PC=2 in supervisor]
  [2] $gc:gc_loop/0 [PC=29 in clause 1]
  [0] system:$c_call_prolog/0 [PC=0 in top query clause]


PROLOG STACK (with arguments; may crash if data is corrupted):
      [4] garbage_collect_clauses
      [2] gc_loop
      [0] '$c_call_prolog'
Running on_halt hooks with status 139
Killing 40969 with default signal handlers
Segmentation fault

Running under gdb:

[Switching to Thread 0x7ffff714f6c0 (LWP 41258)]
unallocClauseIndexTableEntries (ci=0x555568cf3950) at /data2/tmp/swipl-devel-10.1.2/src/pl-index.c:942
942	    { next = cr->next;
(gdb) p cr
$3 = (ClauseRef) 0x126

(gdb) p *cb
$10 = {head = 0x126, tail = 0x0, key = 0, dirty = 0}

So, corrupt linked list?

Looks like it :frowning: Can’t do much without a program to reproduce, so if you can share that? If not, the best you can do is to rebuild Prolog with AddressSanitizer and hope it finds something useful.

Unfortunately I can’t share the code. I’ll see if I can narrow the problem down some.

Note that we can help to fix this commercially with NDA for sharing code, etc. Just contact me privately or bugs@swi-prolog.org. My first hint would be AddressSanitizer or valgrind. That might give a message that gives a clue.

Which platform are we talking about? How did you install/build SWI-Prolog?

I think this might be related to the problem I reported earlier: Dynamic subsumptive table/1 predicate - #9 by alanbur, I’m using maplist + >> lambdas in this case as well and there are some similarities in the stack traces. If I replace all the lambdas in the maplist calls that fail with helper predicates, the problem goes away. And in this version of the app, some of the predicates that fail when compiled aren’t tabled which suggests that tabling likely isn’t a factor.

However I have other maplist + >> usages that don’t fail when compiled, which suggests it might be difficult to get a reproducible test case.

I suspect the SEGV may be a consequence of the initial maplist + >> failure, which is corrupting something somewhere which eventually triggers the SEGV, so I think seeing if AddressSanitizer can pick up anything during that initial failure is the best course of action. But I have a deadline to meet, so that will have to wait…

Linux, git clone + local build. I’ve tried valgrind but it looked like the heat death of the universe will have happened before the app got to the point of failure.

As I said in my earlier post, I’ll see if I can get AddressSanitizer going, but I’ve not used it before, so I’ll need to find & read the cooking instructions first :wink:

For now I have a workaround, which is s/>>/helperPredicate/

In the source tree:

mkdir build.asan
cd build.asan
../scripts/configure
<confirm>
ninja

Now use .../swipl[-devel]/build.asan/src/swipl[-win] instead of swipl[-win] and hope for a crash with a detailed report. The Asan version is 2-3 times slower than the optimized version, which is a lot better than valgrind. They roughly capture the same issues, but sometimes one does the job better than the other.

Thanks, I’ll give that shot when I get a chance. One question - does that require clang? I’m currently using gcc.

No. Asan works with gcc and clang on x86_64 on Linux. It does not work on all architectures.

(Apologies if I’m saying something obvious)

Lambda (>>) generates different code when compiled (there have been various discussions of this) and maplist changes depending on whether you use library(apply_macros) is used, so if you run listing/1 on the predicates in question, you might get a hint as to where the problem is.

Ahah, I think that’s a key bit of info - I was going to ask if there was any difference between interpreted & compiled, because I wasn’t sure. and yes, I’m using apply_macros. I’ll temporarily drop that and see if it makes a difference, and I’ll investigate listing/1 as well - Thanks!

Here’s one discussion I found (I’m pretty sure there were others … you can try searching for “yall” or “lambda”:

tl;dr: library(yall) doesn’t know about other expansions, such as for dicts, so the generated code can be different than what you expect (and there are differences between lambdas in compiled code versus in interpreted code (inside a call/1)). So, it’s worth using listing/1 to see what term expansion did with your code.

This doesn’t account for the SEGV, of course – that shouldn’t happen under any circumstations.

That is fundamental!

W.r.t. library(yall), make sure to load library(apply_macros) and library(yall) explicitly and use listing/1 to verify that the code is compiled. Interpreted yall Lambda expressions are (very) slow and have different semantics w.r.t. variables shared with the remainder of the clause. It is a bit of a fragile library :frowning:

library(lambda) by Ulrich Neumerkel (floating around as pack and on the internet) is more robust, but has an even uglier syntax and is not compiled AFAIK. Lambdas and Prolog don’t go together well as long as Prolog is untyped and homoiconic (source code is data) …

All the places I use lambdas do this:

:- use_module(library(yall)).
:- use_module(library(apply)).
:- use_module(library(apply_macros)).

I understand the comment about lambdas & prolog but mostly I’m using them for things like collecting and iterating over data to produce formatted output. In those cases they are more convenient and IMHO easier to grok than having separate helper predicates.

After further investigation, the code structure and symptoms are consistent with Maplist using a lambda on a lists of dicts doesn't work as expected

The issue is that dict-dot expansion can be hoisted out of a lambda when compiled, the easiest way to check is to use listing/1 on the predicate. If you see something of the form .'(A, b, C), before the maplist (or similar) call rather than within it, then that’s the issue.

I suspect the root cause was the lambda-hoisting issue and the SEGV was secondary. I 've addressed the lambda issue and I haven’t been able to come up with a simple reproducer for the SEGV. I’ll therefore mark this thread as “answered” and start a new one if it happens again.

I’m interested in reproducible code that generates a SEGV as that is pretty much always a bug. But yes, Lambdas, high order predicates and functional notation can lead to surprising results. I’m afraid this cannot be resolved in Prolog.

I’ll circle back when I have time but the code has changed a lot and I’ve not seen the SEGV since. Using dict ‘dot’ notation in conjunction with lambdas seems fraught, for example BUG: internal error in GUI debugger - #5 by alanbur looks like it might be yet another bad interaction between the two?