lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Sat, Jul 4, 2020 at 2:55 PM Stefan <> wrote:
> I'm sorry if this came across negative. The goal of this post is *NOT*
> to exploit the C API, it is to find out how to use it safely and avoid
> the "C API undefined behaviour".


> When this tiny script:
> if coroutine.isyieldable() then
>   coroutine.yield()
> end
> crashes, clearly something went wrong on the C side.

Well, that does not crash the default C host, lua. IMO what is
happening in the C samples is C code managed to put the lua_state in a
state where it can be crashed by running the tiny script.

> So far my conclusions are:
> * Never run the main thread with anything other than lua_call or
>   lua_pcall

IMO this is one thing which you have got wrong. I like to consider it
as if the main thread is running as soon as you do lua_open. It is
just not running lua code. lua_*call just make it run it. If you view
it like this it is less confusing. When I start the REPL an sitting at
the prompt the main thread is running, and waiting in C code. When I
execute io.stdin:read() it is running too, and waiting in C code
invoked from lua code. But it is running.

AAMOF, it you look at coroutine.runnig sources in lcorolib.c you'll
find "  if (L == co) lua_pushliteral(L, "running");".

> * Never lua_yield anything other than the currently running coroutine

I think lua_yield() is not going to be a problem if you pass the
proper argument, the state pointer you normally pass around all your C
code. A lua_state carries a coroutine context, but the main one
carries some other things. The fact that lua_yieldk contains the line
"      luaG_runerror(L, "attempt to yield from outside a coroutine");"
makes me think you would just get an error if you do this and the
state you passed happens to be the main thread.

I suspect what is happening is calling lua_resume on the main thread,
is incorrect but unchecked. This leads to corruption of things which
then lets you call lua_yield avoiding the checks.

In fact I see in your example totally normal results in all cases
except the lua_resume case, where I consider everything after running
lua_resume is meaningless, as that is incorrect, as the main thread
cannot be suspended ( every attempt to yield fails ) and you can only
resume a suspended coroutine ( but lua_resume does not seem to check
that ).

And I am not too sure of the utility of the parameter version of
coroutine.isyieldable(). I mean, only a running coroutine can yield.
You can test a suspended coroutine to see if it could yield as soon as
it starts running, but it can put itself on an unyieldable state ( by
entering a non-yieldable C function) as soon as you resume it, but I
suppose for some cases using external communication mediums it can
work ( maybe pushing a yielding callback or a non yielding one on the
coroutines stack because you know it is going to call that, things
like this ).

> I.e., the answer to "Is it possible to yield/resume the main thread?" is no.

Correct. I thought you were trying to asses the problems of calling
lua_yield in a C callback being unsafe (crashing), which is not, from
what I see it is as safe as calling coroutine.yield in lua as long as
you do not mess with states, the worst that can happen is you get an
error somewhere.

Francisco Olarte.