Commit 7f02b0a0 authored by unknown's avatar unknown
Browse files

Fixed BUG#16887: Cursor causes server segfault

  The problem was a code generation bug: cpop instructions were not generated
  when using ITERATE back to an outer block from a context with a declared
  cursor; this would make it push a new cursor without popping in-between,
  eventually overrunning the cursor stack with a crash as the result.
  Fixed the calculation of how many cursors to pop (in sp_pcontext.cc:
  diff_cursors()), and also corrected diff_cursors() and diff_handlers()
  to when doing a "leave"; don't include the last context we're leaving
  (we are then jumping to the appropriate pop instructions).


mysql-test/r/sp.result:
  Updated result for new test case (BUG#16887)
mysql-test/t/sp.test:
  New test case for BUG#16887
sql/sp_pcontext.cc:
  Added new parameter to sp_pcontext::diff_handlers() and diff_cursors():
  They can either include (for iterate jumps) or exclude (for leave jumps)
  the outer context.
  Fixed bug in diff_cursors(); it was just plain wrong and would return
  zero in some situations when it shouldn't.
sql/sp_pcontext.h:
  Added new parameter to sp_pcontext::diff_handlers() and diff_cursors():
  They can either include (for iterate jumps) or exclude (for leave jumps)
  the outer context.
sql/sql_yacc.yy:
  Added parameter to diff_handlers/diff_cursors depending on if it's an
  iterate or leave jump.
  For "leave", we don't have to include the last context we're leaving since
  we will jump to the appropriate pop instructions.
parent 3f4c176c
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -4519,4 +4519,60 @@ Handler
Inner
drop procedure bug15011|
drop table t3|
drop table if exists t3|
drop procedure if exists bug16887|
create table t3 ( c varchar(1) )|
insert into t3 values
(' '),('.'),(';'),(','),('-'),('_'),('('),(')'),('/'),('\\')|
create procedure bug16887()
begin
declare i int default 10;
again:
while i > 0 do
begin
declare breakchar varchar(1);
declare done int default 0;
declare t3_cursor cursor for select c from t3;
declare continue handler for not found set done = 1;
set i = i - 1;
select i;
if i = 3 then
iterate again;
end if;
open t3_cursor;
loop
fetch t3_cursor into breakchar;
if done = 1 then
begin
close t3_cursor;
iterate again;
end;
end if;
end loop;
end;
end while;
end|
call bug16887()|
i
9
i
8
i
7
i
6
i
5
i
4
i
3
i
2
i
1
i
0
drop table t3|
drop procedure bug16887|
drop table t1,t2;
+54 −0
Original line number Diff line number Diff line
@@ -5311,6 +5311,60 @@ drop procedure bug15011|
drop table t3|


#
# BUG#16887: Cursor causes server segfault
#
--disable_warnings
drop table if exists t3|
drop procedure if exists bug16887|
--enable_warnings

create table t3 ( c varchar(1) )|

insert into t3 values
  (' '),('.'),(';'),(','),('-'),('_'),('('),(')'),('/'),('\\')|

create procedure bug16887()
begin
  declare i int default 10;

 again:
  while i > 0 do
  begin
    declare breakchar varchar(1);
    declare done int default 0;
    declare t3_cursor cursor for select c from t3;
    declare continue handler for not found set done = 1;

    set i = i - 1;
    select i;

    if i = 3 then
      iterate again;
    end if;

    open t3_cursor;

    loop
      fetch t3_cursor into breakchar;

      if done = 1 then
        begin
          close t3_cursor;
          iterate again;
        end;
      end if;
     end loop;
   end;
   end while;
end|

call bug16887()|

drop table t3|
drop procedure bug16887|


#
# BUG#NNNN: New bug synopsis
#
+12 −4
Original line number Diff line number Diff line
@@ -122,30 +122,38 @@ sp_pcontext::pop_context()
}

uint
sp_pcontext::diff_handlers(sp_pcontext *ctx)
sp_pcontext::diff_handlers(sp_pcontext *ctx, bool exclusive)
{
  uint n= 0;
  sp_pcontext *pctx= this;
  sp_pcontext *last_ctx= NULL;

  while (pctx && pctx != ctx)
  {
    n+= pctx->m_handlers;
    last_ctx= pctx;
    pctx= pctx->parent_context();
  }
  if (pctx)
    return n;
    return (exclusive && last_ctx ? n - last_ctx->m_handlers : n);
  return 0;			// Didn't find ctx
}

uint
sp_pcontext::diff_cursors(sp_pcontext *ctx)
sp_pcontext::diff_cursors(sp_pcontext *ctx, bool exclusive)
{
  uint n= 0;
  sp_pcontext *pctx= this;
  sp_pcontext *last_ctx= NULL;

  while (pctx && pctx != ctx)
  {
    n+= pctx->m_cursor.elements;
    last_ctx= pctx;
    pctx= pctx->parent_context();
  }
  if (pctx)
    return ctx->current_cursors() - pctx->current_cursors();
    return  (exclusive && last_ctx ? n - last_ctx->m_cursor.elements : n);
  return 0;			// Didn't find ctx
}

+7 −3
Original line number Diff line number Diff line
@@ -119,11 +119,15 @@ class sp_pcontext : public Sql_alloc
    return m_parent;
  }

  /*
    Number of handlers/cursors to pop between this context and 'ctx'.
    If 'exclusive' is true, don't count the last block we are leaving;
    this is used for LEAVE where we will jump to the cpop/hpop instructions.
  */
  uint
  diff_handlers(sp_pcontext *ctx);

  diff_handlers(sp_pcontext *ctx, bool exclusive);
  uint
  diff_cursors(sp_pcontext *ctx);
  diff_cursors(sp_pcontext *ctx, bool exclusive);


  //
+4 −4
Original line number Diff line number Diff line
@@ -2079,10 +2079,10 @@ sp_proc_stmt:
	      uint ip= sp->instructions();
	      uint n;

	      n= ctx->diff_handlers(lab->ctx);
	      n= ctx->diff_handlers(lab->ctx, TRUE);  /* Exclusive the dest. */
	      if (n)
	        sp->add_instr(new sp_instr_hpop(ip++, ctx, n));
	      n= ctx->diff_cursors(lab->ctx);
	      n= ctx->diff_cursors(lab->ctx, TRUE);  /* Exclusive the dest. */
	      if (n)
	        sp->add_instr(new sp_instr_cpop(ip++, ctx, n));
	      i= new sp_instr_jump(ip, ctx);
@@ -2108,10 +2108,10 @@ sp_proc_stmt:
	      uint ip= sp->instructions();
	      uint n;

	      n= ctx->diff_handlers(lab->ctx);
	      n= ctx->diff_handlers(lab->ctx, FALSE);  /* Inclusive the dest. */
	      if (n)
	        sp->add_instr(new sp_instr_hpop(ip++, ctx, n));
	      n= ctx->diff_cursors(lab->ctx);
	      n= ctx->diff_cursors(lab->ctx, FALSE);  /* Inclusive the dest. */
	      if (n)
	        sp->add_instr(new sp_instr_cpop(ip++, ctx, n));
	      i= new sp_instr_jump(ip, ctx, lab->ip); /* Jump back */