summaryrefslogtreecommitdiff
path: root/testes/cstack.lua
blob: cd74fd281c6a5ec8ba720ec80fbafb38145670fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
-- $Id: testes/cstack.lua $
-- See Copyright Notice in file all.lua

local debug = require "debug"

print"testing C-stack overflow detection"
print"If this test crashes, see its file ('cstack.lua')"

-- Segmentation faults in these tests probably result from a C-stack
-- overflow. To avoid these errors, you can use the function
-- 'debug.setcstacklimit' to set a smaller limit for the use of
-- C stack by Lua. After finding a reliable limit, you might want
-- to recompile Lua with this limit as the value for
-- the constant 'LUAI_MAXCCALLS', which defines the default limit.
-- (The default limit is printed by this test.)
-- Alternatively, you can ensure a larger stack for the program.

-- For Linux, a limit up to 30_000 seems Ok. Windows cannot go much
-- higher than 2_000.


-- get and print original limit
local origlimit = debug.setcstacklimit(400)
print("default stack limit: " .. origlimit)

-- Do the tests using the original limit. Or else you may want to change
-- 'currentlimit' to lower values to avoid a seg. fault or to higher
-- values to check whether they are reliable.
local currentlimit = origlimit
debug.setcstacklimit(currentlimit)
print("current stack limit: " .. currentlimit)


local function checkerror (msg, f, ...)
  local s, err = pcall(f, ...)
  assert(not s and string.find(err, msg))
end

-- auxiliary function to keep 'count' on the screen even if the program
-- crashes.
local count
local back = string.rep("\b", 8)
local function progress ()
  count = count + 1
  local n = string.format("%-8d", count)
  io.stderr:write(back, n)   -- erase previous value and write new one
end


do    print("testing simple recursion:")
  count = 0
  local function foo ()
    progress()
    foo()   -- do recursive calls until a stack error (or crash)
  end
  checkerror("stack overflow", foo)
  print("\tfinal count: ", count)
end


do  print("testing stack overflow in message handling")
  count = 0
  local function loop (x, y, z)
    progress()
    return 1 + loop(x, y, z)
  end
  local res, msg = xpcall(loop, loop)
  assert(msg == "error in error handling")
  print("\tfinal count: ", count)
end


-- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
do  print("testing recursion inside pattern matching")
  local function f (size)
    local s = string.rep("a", size)
    local p = string.rep(".?", size)
    return string.match(s, p)
  end
  local m = f(80)
  assert(#m == 80)
  checkerror("too complex", f, 200000)
end


do  print("testing stack-overflow in recursive 'gsub'")
  count = 0
  local function foo ()
    progress()
    string.gsub("a", ".", foo)
  end
  checkerror("stack overflow", foo)
  print("\tfinal count: ", count)

  print("testing stack-overflow in recursive 'gsub' with metatables")
  count = 0
  local t = setmetatable({}, {__index = foo})
  foo = function ()
    count = count + 1
    progress(count)
    string.gsub("a", ".", t)
  end
  checkerror("stack overflow", foo)
  print("\tfinal count: ", count)
end


do  print("testing changes in C-stack limit")

  assert(not debug.setcstacklimit(0))        -- limit too small
  assert(not debug.setcstacklimit(50000))    -- limit too large
  local co = coroutine.wrap (function ()
               return debug.setcstacklimit(400)
             end)
  assert(co() == false)         -- cannot change C stack inside coroutine

  local n
  local function foo () n = n + 1; foo () end

  local function check ()
    n = 0
    pcall(foo)
    return n
  end

  -- set limit to 400
  assert(debug.setcstacklimit(400) == currentlimit)
  local lim400 = check()
  -- set a very low limit (given that there are already several active
  -- calls to arrive here)
  local lowlimit = 38
  assert(debug.setcstacklimit(lowlimit) == 400)
  assert(check() < lowlimit - 30)
  assert(debug.setcstacklimit(600) == lowlimit)
  local lim600 = check()
  assert(lim600 == lim400 + 200)


  -- 'setcstacklimit' works inside protected calls. (The new stack
  -- limit is kept when 'pcall' returns.)
  assert(pcall(function ()
    assert(debug.setcstacklimit(400) == 600)
    assert(check() <= lim400)
  end))

  assert(check() == lim400)
  assert(debug.setcstacklimit(origlimit) == 400)   -- restore original limit
end


print'OK'