cstack.lua 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. -- $Id: testes/cstack.lua $
  2. -- See Copyright Notice in file all.lua
  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
  71. print("nesting of resuming yielded coroutines")
  72. local count = 0
  73. local function body ()
  74. coroutine.yield()
  75. local f = coroutine.wrap(body)
  76. f(); -- start new coroutine (will stop in previous yield)
  77. count = count + 1
  78. f() -- call it recursively
  79. end
  80. local f = coroutine.wrap(body)
  81. f()
  82. assert(not pcall(f))
  83. print("final count: ", count)
  84. end
  85. do -- bug in 5.4.2
  86. print("nesting coroutines running after recoverable errors")
  87. local count = 0
  88. local function foo()
  89. count = count + 1
  90. pcall(1) -- create an error
  91. -- running now inside 'precover' ("protected recover")
  92. coroutine.wrap(foo)() -- call another coroutine
  93. end
  94. checkerror("C stack overflow", foo)
  95. print("final count: ", count)
  96. end
  97. if T then
  98. print("testing stack recovery")
  99. local N = 0 -- trace number of calls
  100. local LIM = -1 -- will store N just before stack overflow
  101. -- trace stack size; after stack overflow, it should be
  102. -- the maximum allowed stack size.
  103. local stack1
  104. local dummy
  105. local function err(msg)
  106. assert(string.find(msg, "stack overflow"))
  107. local _, stacknow = T.stacklevel()
  108. assert(stacknow == stack1 + 200)
  109. end
  110. -- When LIM==-1, the 'if' is not executed, so this function only
  111. -- counts and stores the stack limits up to overflow. Then, LIM
  112. -- becomes N, and then the 'if' code is run when the stack is
  113. -- full. Then, there is a stack overflow inside 'xpcall', after which
  114. -- the stack must have been restored back to its maximum normal size.
  115. local function f()
  116. dummy, stack1 = T.stacklevel()
  117. if N == LIM then
  118. xpcall(f, err)
  119. local _, stacknow = T.stacklevel()
  120. assert(stacknow == stack1)
  121. return
  122. end
  123. N = N + 1
  124. f()
  125. end
  126. local topB, sizeB -- top and size Before overflow
  127. local topA, sizeA -- top and size After overflow
  128. topB, sizeB = T.stacklevel()
  129. tracegc.stop() -- __gc should not be called with a full stack
  130. xpcall(f, err)
  131. tracegc.start()
  132. topA, sizeA = T.stacklevel()
  133. -- sizes should be comparable
  134. assert(topA == topB and sizeA < sizeB * 2)
  135. print(string.format("maximum stack size: %d", stack1))
  136. LIM = N -- will stop recursion at maximum level
  137. N = 0 -- to count again
  138. tracegc.stop() -- __gc should not be called with a full stack
  139. f()
  140. tracegc.start()
  141. print"+"
  142. end
  143. print'OK'