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.

1 Like

(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.