commit fa2e3415185a28542d419a641ecd6cddd52e3cd9 Author: Mark Wielaard Date: Wed May 11 15:27:48 2011 -0400 CVE-2011-1781, CVE-2011-1769: correct DW_OP_{mod,div} division-by-zero bug Probing a process with corrupted DWARF information, it has been possible to create a kernel-side divison-by-zero. This fixes. Handle DW_OP_div/mod divide by zero. DW_OP_mod should work unsigned. * loc2c.c (translate): Use helper functions div_op and mod_op for DW_OP_div and DW_OP_mod operands. Set used_deref = true. * translate.cxx (translate_runtime): Emit STAP_MSG_LOC2C_03 define. * runtime/loc2c-runtime.h: Define dwarf_div_op and dwarf_mod_op macros. * runtime/unwind.c (compute_expr): Check for zero before executing DW_OP_mod or DW_OP_div. diff --git a/loc2c.c b/loc2c.c index 331090c..5f0dd09 100644 --- a/loc2c.c +++ b/loc2c.c @@ -681,7 +681,6 @@ translate (struct location_context *ctx, int indent, UNOP (abs, op_abs); BINOP (and, &); BINOP (minus, -); - BINOP (mod, %); BINOP (mul, *); UNOP (neg, -); UNOP (not, ~); @@ -716,9 +715,21 @@ translate (struct location_context *ctx, int indent, { POP (b); POP (a); - push ("(%s) " STACKFMT " / (%s)" STACKFMT, + push ("dwarf_div_op((%s) " STACKFMT ", (%s) " STACKFMT ")", stack_slot_type (loc, true), a, stack_slot_type (loc, true), b); + used_deref = true; + break; + } + + case DW_OP_mod: + { + POP (b); + POP (a); + push ("dwarf_mod_op((%s) " STACKFMT ", (%s) " STACKFMT ")", + stack_slot_type (loc, false), a, + stack_slot_type (loc, false), b); + used_deref = true; break; } diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h index d511087..968045f 100644 --- a/runtime/loc2c-runtime.h +++ b/runtime/loc2c-runtime.h @@ -82,6 +82,28 @@ }) #endif +/* dwarf_div_op and dwarf_mod_op do division and modulo operations catching any + divide by zero issues. When they detect div_by_zero they "fault" + by jumping to the (slightly misnamed) deref_fault label. */ +#define dwarf_div_op(a,b) ({ \ + if (b == 0) { \ + snprintf(c->error_buffer, sizeof(c->error_buffer), \ + "divide by zero in DWARF operand (%s)", "DW_OP_div"); \ + c->last_error = c->error_buffer; \ + goto deref_fault; \ + } \ + a / b; \ +}) +#define dwarf_mod_op(a,b) ({ \ + if (b == 0) { \ + snprintf(c->error_buffer, sizeof(c->error_buffer), \ + "divide by zero in DWARF operand (%s)", "DW_OP_mod"); \ + c->last_error = c->error_buffer; \ + goto deref_fault; \ + } \ + a % b; \ +}) + /* PR 10601: user-space (user_regset) register access. */ #if defined(STAPCONF_REGSET) #include diff --git a/runtime/unwind.c b/runtime/unwind.c index 3e56965..810d9eb 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -856,12 +856,26 @@ static int compute_expr(const u8 *expr, struct unwind_frame_info *frame, BINOP(plus, +); BINOP(minus, -); BINOP(mul, *); - BINOP(div, /); - BINOP(mod, %); BINOP(shl, <<); BINOP(shra, >>); #undef BINOP + case DW_OP_mod: { + unsigned long b = POP; + unsigned long a = POP; + if (b == 0) + goto divzero; + PUSH (a % b); + } + + case DW_OP_div: { + long b = POP; + long a = POP; + if (b == 0) + goto divzero; + PUSH (a / b); + } + case DW_OP_shr: { unsigned long b = POP; unsigned long a = POP; @@ -944,6 +958,9 @@ overflow: underflow: _stp_warn("DWARF expression stack underflow in CFI\n"); return 1; +divzero: + _stp_warn("DWARF expression stack divide by zero in CFI\n"); + return 1; #undef NEED #undef PUSH