cstack.lua 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. -- $Id: testes/cstack.lua $
  2. -- See Copyright Notice in file lua.h
  3. local tracegc = require"tracegc"
  4. print"testing stack overflow detection"
  5. -- Segmentation faults in these tests probably result from a C-stack
  6. -- overflow. To avoid these errors, you should set a smaller limit for
  7. -- the use of C stack by Lua, by changing the constant 'LUAI_MAXCCALLS'.
  8. -- Alternatively, you can ensure a larger stack for the program.
  9. local function checkerror (msg, f, ...)
  10. local s, err = pcall(f, ...)
  11. assert(not s and string.find(err, msg))
  12. end
  13. do print("testing stack overflow in message handling")
  14. local count = 0
  15. local function loop (x, y, z)
  16. count = count + 1
  17. return 1 + loop(x, y, z)
  18. end
  19. tracegc.stop() -- __gc should not be called with a full stack
  20. local res, msg = xpcall(loop, loop)
  21. tracegc.start()
  22. assert(msg == "error in error handling")
  23. print("final count: ", count)
  24. end
  25. -- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
  26. do print("testing recursion inside pattern matching")
  27. local function f (size)
  28. local s = string.rep("a", size)
  29. local p = string.rep(".?", size)
  30. return string.match(s, p)
  31. end
  32. local m = f(80)
  33. assert(#m == 80)
  34. checkerror("too complex", f, 2000)
  35. end
  36. do print("testing stack-overflow in recursive 'gsub'")
  37. local count = 0
  38. local function foo ()
  39. count = count + 1
  40. string.gsub("a", ".", foo)
  41. end
  42. checkerror("stack overflow", foo)
  43. print("final count: ", count)
  44. print("testing stack-overflow in recursive 'gsub' with metatables")
  45. local count = 0
  46. local t = setmetatable({}, {__index = foo})
  47. foo = function ()
  48. count = count + 1
  49. string.gsub("a", ".", t)
  50. end
  51. checkerror("stack overflow", foo)
  52. print("final count: ", count)
  53. end
  54. do -- bug in 5.4.0
  55. print("testing limits in coroutines inside deep calls")
  56. local count = 0
  57. local lim = 1000
  58. local function stack (n)
  59. if n > 0 then return stack(n - 1) + 1
  60. else coroutine.wrap(function ()
  61. count = count + 1
  62. stack(lim)
  63. end)()
  64. end
  65. end
  66. local st, msg = xpcall(stack, function () return "ok" end, lim)
  67. assert(not st and msg == "ok")
  68. print("final count: ", count)
  69. end
  70. do -- bug since 5.4.0
  71. local count = 0
  72. print("chain of 'coroutine.close'")
  73. -- create N coroutines forming a list so that each one, when closed,
  74. -- closes the previous one. (With a large enough N, previous Lua
  75. -- versions crash in this test.)
  76. local coro = false
  77. for i = 1, 1000 do
  78. local previous = coro
  79. coro = coroutine.create(function()
  80. local cc <close> = setmetatable({}, {__close=function()
  81. count = count + 1
  82. if previous then
  83. assert(coroutine.close(previous))
  84. end
  85. end})
  86. coroutine.yield() -- leaves 'cc' pending to be closed
  87. end)
  88. assert(coroutine.resume(coro)) -- start it and run until it yields
  89. end
  90. local st, msg = coroutine.close(coro)
  91. assert(not st and string.find(msg, "C stack overflow"))
  92. print("final count: ", count)
  93. end
  94. do
  95. print("nesting of resuming yielded coroutines")
  96. local count = 0
  97. local function body ()
  98. coroutine.yield()
  99. local f = coroutine.wrap(body)
  100. f(); -- start new coroutine (will stop in previous yield)
  101. count = count + 1
  102. f() -- call it recursively
  103. end
  104. local f = coroutine.wrap(body)
  105. f()
  106. assert(not pcall(f))
  107. print("final count: ", count)
  108. end
  109. do -- bug in 5.4.2
  110. print("nesting coroutines running after recoverable errors")
  111. local count = 0
  112. local function foo()
  113. count = count + 1
  114. pcall(1) -- create an error
  115. -- running now inside 'precover' ("protected recover")
  116. coroutine.wrap(foo)() -- call another coroutine
  117. end
  118. checkerror("C stack overflow", foo)
  119. print("final count: ", count)
  120. end
  121. if T then
  122. print("testing stack recovery")
  123. local N = 0 -- trace number of calls
  124. local LIM = -1 -- will store N just before stack overflow
  125. -- trace stack size; after stack overflow, it should be
  126. -- the maximum allowed stack size.
  127. local stack1
  128. local dummy
  129. local function err(msg)
  130. assert(string.find(msg, "stack overflow"))
  131. local _, stacknow = T.stacklevel()
  132. assert(stacknow == stack1 + 200)
  133. end
  134. -- When LIM==-1, the 'if' is not executed, so this function only
  135. -- counts and stores the stack limits up to overflow. Then, LIM
  136. -- becomes N, and then the 'if' code is run when the stack is
  137. -- full. Then, there is a stack overflow inside 'xpcall', after which
  138. -- the stack must have been restored back to its maximum normal size.
  139. local function f()
  140. dummy, stack1 = T.stacklevel()
  141. if N == LIM then
  142. xpcall(f, err)
  143. local _, stacknow = T.stacklevel()
  144. assert(stacknow == stack1)
  145. return
  146. end
  147. N = N + 1
  148. f()
  149. end
  150. local topB, sizeB -- top and size Before overflow
  151. local topA, sizeA -- top and size After overflow
  152. topB, sizeB = T.stacklevel()
  153. tracegc.stop() -- __gc should not be called with a full stack
  154. xpcall(f, err)
  155. tracegc.start()
  156. topA, sizeA = T.stacklevel()
  157. -- sizes should be comparable
  158. assert(topA == topB and sizeA < sizeB * 2)
  159. print(string.format("maximum stack size: %d", stack1))
  160. LIM = N -- will stop recursion at maximum level
  161. N = 0 -- to count again
  162. tracegc.stop() -- __gc should not be called with a full stack
  163. f()
  164. tracegc.start()
  165. print"+"
  166. end
  167. print'OK'