Package: lua-lpeg
Version: 1.0.0-2
Severity: normal
Tags: patch

Dear Maintainer,

under certain conditions, lpeg will crash while walking the pattern tree 
looking for TCapture nodes.

The reproducer, taken from the upstream discussion, is:

root@sid-lua:~# cat repro.lua
#!/usr/bin/env lua
lpeg = require "lpeg"

p = lpeg.C(-lpeg.P{lpeg.P'x' * lpeg.V(1) + lpeg.P'y'})
p:match("xx")

The program crashes due to a hascaptures() infinite recursion:

root@sid-lua:~# ./repro.lua
Segmentation fault (core dumped)

(gdb) bt -25
#523984 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523985 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523986 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523987 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523988 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523989 0x00007ffff7a3743c in hascaptures () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523990 0x00007ffff7a3815c in ?? () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523991 0x00007ffff7a388e3 in compile () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523992 0x00007ffff7a36fab in ?? () from 
/usr/lib/x86_64-linux-gnu/lua/5.2/lpeg.so
#523993 0x000055555555fd1e in ?? ()
#523994 0x000055555556a5fc in ?? ()
#523995 0x00005555555600c8 in ?? ()
#523996 0x000055555555f63f in ?? ()
#523997 0x000055555556030f in ?? ()
#523998 0x000055555555dc91 in lua_pcallk ()
#523999 0x000055555555b896 in ?? ()
#524000 0x000055555555c54b in ?? ()
#524001 0x000055555555fd1e in ?? ()
#524002 0x0000555555560092 in ?? ()
#524003 0x000055555555f63f in ?? ()
#524004 0x000055555556030f in ?? ()
#524005 0x000055555555dc91 in lua_pcallk ()
#524006 0x000055555555b64b in ?? ()
#524007 0x00007ffff7c94bbb in __libc_start_main (main=0x55555555b5f0, argc=2, 
argv=0x7fffffffe6d8, init=<optimized out>, fini=<optimized out>, 
rtld_fini=<optimized out>, stack_end=0x7fffffffe6c8)
    at ../csu/libc-start.c:308
#524008 0x000055555555b70a in ?? ()

The expected behavior is to have the program finish normally

This was reproduced in Debian Sid, and the versions of lua5.2 and lua-lpeg are:

root@sid-lua:~# dpkg -l | grep lua
ii  lua-lpeg:amd64           1.0.0-2                    amd64        LPeg 
library for the Lua language
ii  lua5.2                   5.2.4-1.1+b3               amd64        Simple, 
extensible, embeddable programming language

This was fixed upstream in 1.0.1 by stopping the recursion in TCall nodes and 
controlling that TRule nodes do not follow siblings (sib2). Attached there's a 
backported patch from upstream that should fix the issue.

The upstream discussion can be found here: 
http://lua.2524044.n2.nabble.com/LPeg-intermittent-stack-exhaustion-td7674831.html

Thanks for considering the patch!

Victor
diff -Nru lua-lpeg-1.0.0/debian/patches/series 
lua-lpeg-1.0.0/debian/patches/series
--- lua-lpeg-1.0.0/debian/patches/series        2018-02-23 12:39:59.000000000 
+0100
+++ lua-lpeg-1.0.0/debian/patches/series        2019-10-02 17:49:19.000000000 
+0200
@@ -0,0 +1 @@
+stop-hascaptures-recursion.patch
diff -Nru lua-lpeg-1.0.0/debian/patches/stop-hascaptures-recursion.patch 
lua-lpeg-1.0.0/debian/patches/stop-hascaptures-recursion.patch
--- lua-lpeg-1.0.0/debian/patches/stop-hascaptures-recursion.patch      
1970-01-01 01:00:00.000000000 +0100
+++ lua-lpeg-1.0.0/debian/patches/stop-hascaptures-recursion.patch      
2019-10-02 17:49:19.000000000 +0200
@@ -0,0 +1,59 @@
+From: Victor Tapia <victor.ta...@canonical.com>
+Date: Tue, 8 Oct 2014 18:44:17 +0200
+Origin: backport, http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.0.1.tar.gz
+Description: Stop infinite recursion in hascaptures()
+ Stop recursion in TCall nodes and control that TRule nodes do not follow sib2
+
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+bug/1580385
+
+---
+--- a/lpcode.c
++++ b/lpcode.c
+@@ -126,6 +126,27 @@
+ 
+ 
+ /*
++** Visit a TCall node taking care to stop recursion. If node not yet
++** visited, return 'f(sib2(tree))', otherwise return 'def' (default
++** value)
++*/
++static int callrecursive (TTree *tree, int f (TTree *t), int def) {
++  int key = tree->key;
++  assert(tree->tag == TCall);
++  assert(sib2(tree)->tag == TRule);
++  if (key == 0)  /* node already visited? */
++    return def;  /* return default value */
++  else {  /* first visit */
++    int result;
++    tree->key = 0;  /* mark call as already visited */
++    result = f(sib2(tree));  /* go to called rule */
++    tree->key = key;  /* restore tree */
++    return result;
++  }
++}
++
++
++/*
+ ** Check whether a pattern tree has captures
+ */
+ int hascaptures (TTree *tree) {
+@@ -134,14 +155,17 @@
+     case TCapture: case TRunTime:
+       return 1;
+     case TCall:
+-      tree = sib2(tree); goto tailcall;  /* return hascaptures(sib2(tree)); */
++      return callrecursive(tree, hascaptures, 0);
++    case TRule:  /* do not follow siblings */
++      tree = sib1(tree); goto tailcall;
+     case TOpenCall: assert(0);
+     default: {
+       switch (numsiblings[tree->tag]) {
+         case 1:  /* return hascaptures(sib1(tree)); */
+           tree = sib1(tree); goto tailcall;
+         case 2:
+-          if (hascaptures(sib1(tree))) return 1;
++          if (hascaptures(sib1(tree)))
++            return 1;
+           /* else return hascaptures(sib2(tree)); */
+           tree = sib2(tree); goto tailcall;
+         default: assert(numsiblings[tree->tag] == 0); return 0;

Reply via email to