lj_gc_step_jit bug

  • From: "Cheng, Long" <long.x.cheng@xxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Tue, 05 Nov 2013 22:11:01 +0800

hi all:
We think we found a bug in lj_gc_step_jit, here is the detail:

int LJ_FASTCALL lj_gc_step_jit(global_State *g, MSize steps)
{
  lua_State *L = gco2th(gcref(g->jit_L));
  L->base = mref(G(L)->jit_base, TValue);
  L->top = curr_topL(L);
  while (steps-- > 0 && lj_gc_step(L) == 0)
    ;
  /* Return 1 to force a trace exit. */
  return (G(L)->gc.state == GCSatomic || G(L)->gc.state == GCSfinalize);
}

In luajit generated jit code (from asm_gc_check() ), before calls lj_gc_step_jit(), the jitted code will compare gc.total and gc.threshold and make sure it only calls lj_gc_step_jit() when gc.threshold is less than or equal to gc.total. In case lj_gc_step_jit() is called with steps > 1, lj_gc_step() will be called multiple times in the while loop above, but without checking if gc.threshold is less than or equal to gc.total each time. We observed situation where when steps = 2, the first call into lj_gc_step() may substract gc.debt to less than GCSTEPSIZE and then gc.threshold will be set to gc.total + GCSTEPSIZE. In this case, next call to lj_gc_step() from the above while loop will have the situation where gc.threshold is larger than gc.total and the line "g->gc.debt += g->gc.total - g->gc.threshold;" in lj_gc_step() will set gc.debt to a very large number (negative signed integer), which is very bad because following LUA_GCSTEP calls will try to do a very big number of steps.

Regards
Long

Other related posts: