Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Evergreen:11.2
systemtap
systemtap-CVE-2009-2911.diff
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File systemtap-CVE-2009-2911.diff of Package systemtap
From: Jan Lieskovsky <jlieskov@redhat.com> Subject: Three SystemTap-1.0 denial of service issues References: CVE-2009-2911, BNC#548361 Upstream: yes Three denial of service flaws were found in the SystemTap instrumentation system of version 1.0, when the --unprivileged mode was activated: b, Kernel stack frame overflow allows local attackers to cause denial of service via specially-crafted user-provided DWARF information. a, Kernel stack overflow allows local attackers to cause denial of service or execute arbitrary code via long number of parameters, provided to the print* call. c, Absent check(s) for the upper bound of the size of the unwind table and for the upper bound of the size of each of the CIE/CFI records, could allow an attacker to cause a denial of service (infinite loop). --- buildrun.cxx | 3 ++ dwflpp.cxx | 10 ++++++++- loc2c-test.c | 7 ++++-- loc2c.c | 19 +++++++++++++----- loc2c.h | 3 +- runtime/unwind.c | 43 +++++++++++++++++++++++++++++++++++------- runtime/unwind/unwind.h | 6 +++-- testsuite/transko/varargs.stp | 10 +++++++++ testsuite/transok/varargs.stp | 9 ++++++++ translate.cxx | 16 +++++++++++++++ 10 files changed, 108 insertions(+), 18 deletions(-) --- a/buildrun.cxx +++ b/buildrun.cxx @@ -200,6 +200,9 @@ compile_pass (systemtap_session& s) // o << "CFLAGS += -fno-unit-at-a-time" << endl; + // 512 bytes should be enough for anybody + o << "EXTRA_CFLAGS += $(call cc-option,-Wframe-larger-than=512)" << endl; + // Assumes linux 2.6 kbuild o << "EXTRA_CFLAGS += -Wno-unused -Werror" << endl; #if CHECK_POINTER_ARITH_PR5947 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -2272,7 +2272,15 @@ dwflpp::express_as_string (string prelud fprintf(memstream, "{\n"); fprintf(memstream, "%s", prelude.c_str()); - bool deref = c_emit_location (memstream, head, 1); + + unsigned int stack_depth; + bool deref = c_emit_location (memstream, head, 1, &stack_depth); + + // Ensure that DWARF keeps loc2c to a "reasonable" stack size + // 32 intptr_t leads to max 256 bytes on the stack + if (stack_depth > 32) + throw semantic_error("oversized DWARF stack"); + fprintf(memstream, "%s", postlude.c_str()); fprintf(memstream, " goto out;\n"); --- a/loc2c-test.c +++ b/loc2c-test.c @@ -329,11 +329,14 @@ handle_variable (Dwarf_Die *lscopes, int "{\n" " intptr_t value;"); - bool deref = c_emit_location (stdout, head, 1); + unsigned int stack_depth; + bool deref = c_emit_location (stdout, head, 1, &stack_depth); obstack_free (&pool, NULL); - puts (store ? " return;" : + printf (" /* max expression stack depth %u */\n", stack_depth); + + puts (store ? " return;" : " printk (\" ---> %ld\\n\", (unsigned long) value);\n" " return;"); --- a/loc2c.c +++ b/loc2c.c @@ -2071,7 +2071,8 @@ emit_loc_address (FILE *out, struct loca assign it to an address-sized value. */ static void emit_loc_value (FILE *out, struct location *loc, unsigned int indent, - const char *target, bool declare) + const char *target, bool declare, + bool *used_deref, unsigned int *max_stack) { if (declare) emit ("%*s%s %s;\n", indent * 2, "", STACK_TYPE, target); @@ -2091,6 +2092,9 @@ emit_loc_value (FILE *out, struct locati case loc_address: case loc_value: emit_loc_address (out, loc, indent, target); + *used_deref = *used_deref || loc->address.used_deref; + if (loc->address.stack_depth > *max_stack) + *max_stack = loc->address.stack_depth; break; } @@ -2098,7 +2102,8 @@ emit_loc_value (FILE *out, struct locati } bool -c_emit_location (FILE *out, struct location *loc, int indent) +c_emit_location (FILE *out, struct location *loc, int indent, + unsigned int *max_stack) { emit ("%*s{\n", indent * 2, ""); @@ -2134,9 +2139,11 @@ c_emit_location (FILE *out, struct locat } bool deref = false; + *max_stack = 0; if (loc->frame_base != NULL) - emit_loc_value (out, loc->frame_base, indent, "frame_base", true); + emit_loc_value (out, loc->frame_base, indent, "frame_base", true, + &deref, max_stack); for (; loc->next != NULL; loc = loc->next) switch (loc->type) @@ -2144,8 +2151,7 @@ c_emit_location (FILE *out, struct locat case loc_address: case loc_value: /* Emit the program fragment to calculate the address. */ - emit_loc_value (out, loc, indent + 1, "addr", false); - deref = deref || loc->address.used_deref; + emit_loc_value (out, loc, indent + 1, "addr", false, &deref, max_stack); break; case loc_fragment: @@ -2172,6 +2178,9 @@ c_emit_location (FILE *out, struct locat emit ("%s%*s}\n", loc->address.program, indent * 2, ""); + if (loc->address.stack_depth > *max_stack) + *max_stack = loc->address.stack_depth; + return deref || loc->address.used_deref; } --- a/loc2c.h +++ b/loc2c.h @@ -112,6 +112,7 @@ struct location *c_translate_argument (s Writes complete lines of C99, code forming a complete C block, to STREAM. Return value is true iff that code uses the `deref' runtime macros. */ -bool c_emit_location (FILE *stream, struct location *loc, int indent); +bool c_emit_location (FILE *stream, struct location *loc, int indent, + unsigned int *max_stack); /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -88,7 +88,7 @@ static sleb128_t get_sleb128(const u8 ** /* given an FDE, find its CIE */ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, - int is_ehframe) + uint32_t table_len, int is_ehframe) { const u32 *cie; @@ -118,6 +118,11 @@ static const u32 *cie_for_fde(const u32 else cie = unwind_data + fde[1]; + /* Make sure address falls in the table */ + if (((void *)cie) < ((void*)unwind_data) + || ((void*)cie) > ((void*)(unwind_data + table_len))) + return NULL; + if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) || (*cie & (sizeof(*cie) - 1)) || (cie[1] != 0xffffffff && cie[1] != 0)) { @@ -200,7 +205,8 @@ static unsigned long read_pointer(const return value; } -static signed fde_pointer_type(const u32 *cie) +static signed fde_pointer_type(const u32 *cie, void *unwind_data, + uint32_t table_len) { const u8 *ptr = (const u8 *)(cie + 2); unsigned version = *ptr; @@ -212,11 +218,16 @@ static signed fde_pointer_type(const u32 const u8 *end = (const u8 *)(cie + 1) + *cie; uleb128_t len; + /* end of cie should fall within unwind table. */ + if (((void*)end) < ((void *)unwind_data) + || ((void *)end) > ((void *)(unwind_data + table_len))) + return -1; + /* check if augmentation size is first (and thus present) */ if (*ptr != 'z') return -1; /* check if augmentation string is nul-terminated */ - if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) + if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) return -1; ++ptr; /* skip terminator */ get_uleb128(&ptr, end); /* skip code alignment */ @@ -267,6 +278,10 @@ static void set_rule(uleb128_t reg, enum } } +/* Limit the number of instructions we process. Arbitrary limit. + 512 should be enough for anybody... */ +#define MAX_CFI 512 + static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, signed ptrType, struct unwind_state *state) { union { @@ -276,6 +291,9 @@ static int processCFI(const u8 *start, c } ptr; int result = 1; + if (end - start > MAX_CFI) + return 0; + dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc); if (start != state->cieStart) { state->loc = state->org; @@ -606,10 +624,10 @@ static int unwind_frame(struct unwind_fr /* found the fde, now set startLoc and endLoc */ if (fde != NULL) { - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (likely(cie != NULL && cie != &bad_cie && cie != ¬_fde)) { ptr = (const u8 *)(fde + 2); - ptrType = fde_pointer_type(cie); + ptrType = fde_pointer_type(cie, table, table_len); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); startLoc = adjustStartLoc(startLoc, m, s, ptrType, is_ehframe); @@ -632,12 +650,12 @@ static int unwind_frame(struct unwind_fr for (fde = table, tableSize = table_len; cie = NULL, tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { dbug_unwind(3, "fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize); - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (cie == &bad_cie) { cie = NULL; break; } - if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie)) < 0) + if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie, table, table_len)) < 0) continue; ptr = (const u8 *)(fde + 2); @@ -666,6 +684,12 @@ static int unwind_frame(struct unwind_fr state.cieEnd = ptr; /* keep here temporarily */ ptr = (const u8 *)(cie + 2); end = (const u8 *)(cie + 1) + *cie; + + /* end should fall within unwind table. */ + if (((void *)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + frame->call_frame = 1; if ((state.version = *ptr) != 1) { dbug_unwind(1, "CIE version number is %d. 1 is supported.\n", state.version); @@ -723,6 +747,11 @@ static int unwind_frame(struct unwind_fr state.cieEnd = end; end = (const u8 *)(fde + 1) + *fde; + /* end should fall within unwind table. */ + if (((void*)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + /* skip augmentation */ if (((const char *)(cie + 2))[1] == 'z') { uleb128_t augSize = get_uleb128(&ptr, end); --- a/runtime/unwind/unwind.h +++ b/runtime/unwind/unwind.h @@ -143,8 +143,10 @@ static unsigned long read_pointer(const const void *end, signed ptrType); static const u32 bad_cie, not_fde; -static const u32 *cie_for_fde(const u32 *fde, void *table, int is_ehframe); -static signed fde_pointer_type(const u32 *cie); +static const u32 *cie_for_fde(const u32 *fde, void *table, + uint32_t table_len, int is_ehframe); +static signed fde_pointer_type(const u32 *cie, + void *table, uint32_t table_len); #endif /* STP_USE_DWARF_UNWINDER */ --- /dev/null +++ b/testsuite/transko/varargs.stp @@ -0,0 +1,10 @@ +#! stap -p3 + +probe begin { + // PR10750 enforces at most 32 print args + println(1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, + 33) +} --- /dev/null +++ b/testsuite/transok/varargs.stp @@ -0,0 +1,9 @@ +#! stap -p3 + +probe begin { + // PR10750 enforces at most 32 print args + println(1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32) +} --- a/translate.cxx +++ b/translate.cxx @@ -28,6 +28,11 @@ extern "C" { #include <elfutils/libdwfl.h> } +// Max unwind table size (debug or eh) per module. Somewhat arbitrary +// limit (a bit more than twice the .debug_frame size of my local +// vmlinux for 2.6.31.4-83.fc12.x86_64) +#define MAX_UNWIND_TABLE_SIZE (3 * 1024 * 1024) + using namespace std; struct var; @@ -4151,6 +4156,11 @@ c_unparser::visit_print_format (print_fo { stmt_expr block(*this); + // PR10750: Enforce a reasonable limit on # of varargs + // 32 varargs leads to max 256 bytes on the stack + if (e->args.size() > 32) + throw semantic_error("too many arguments to print", e->tok); + // Compute actual arguments vector<tmpvar> tmp; @@ -4763,6 +4773,9 @@ dump_unwindsyms (Dwfl_Module *m, get_unwind_data (m, &debug_frame, &eh_frame, &debug_len, &eh_len, &eh_addr); if (debug_frame != NULL && debug_len > 0) { + if (debug_len > MAX_UNWIND_TABLE_SIZE) + throw semantic_error ("module debug unwind table size too big"); + c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n"; c->output << "static uint8_t _stp_module_" << stpmod_idx << "_debug_frame[] = \n"; @@ -4780,6 +4793,9 @@ dump_unwindsyms (Dwfl_Module *m, if (eh_frame != NULL && eh_len > 0) { + if (eh_len > MAX_UNWIND_TABLE_SIZE) + throw semantic_error ("module eh unwind table size too big"); + c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n"; c->output << "static uint8_t _stp_module_" << stpmod_idx << "_eh_frame[] = \n";
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor