rpms/systemtap/F-11 SystemTap-1.0-limit-dwarf-expression-stack-size.patch, NONE, 1.1 SystemTap-1.0-limit-printf-arguments.patch, NONE, 1.1 SystemTap-1.0-unwind-table-size-checks.patch, NONE, 1.1 systemtap.spec, 1.51, 1.52

Josh Stone jistone at fedoraproject.org
Wed Oct 21 15:52:43 UTC 2009


Author: jistone

Update of /cvs/pkgs/rpms/systemtap/F-11
In directory cvs1.fedora.phx.redhat.com:/tmp/cvs-serv23664

Modified Files:
	systemtap.spec 
Added Files:
	SystemTap-1.0-limit-dwarf-expression-stack-size.patch 
	SystemTap-1.0-limit-printf-arguments.patch 
	SystemTap-1.0-unwind-table-size-checks.patch 
Log Message:
Fixes for CVE-2009-2911

SystemTap-1.0-limit-dwarf-expression-stack-size.patch:
 dwflpp.cxx   |   10 +++++++++-
 loc2c-test.c |    7 +++++--
 loc2c.c      |   19 ++++++++++++++-----
 loc2c.h      |    3 ++-
 4 files changed, 30 insertions(+), 9 deletions(-)

--- NEW FILE SystemTap-1.0-limit-dwarf-expression-stack-size.patch ---
diff --git a/dwflpp.cxx b/dwflpp.cxx
index 636cd38..c31548d 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -2272,7 +2272,15 @@ dwflpp::express_as_string (string prelude,
 
   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");
 
diff --git a/loc2c-test.c b/loc2c-test.c
index 495a95f..ed7aa4b 100644
--- a/loc2c-test.c
+++ b/loc2c-test.c
@@ -329,11 +329,14 @@ handle_variable (Dwarf_Die *lscopes, int lnscopes, int out,
 	"{\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;");
 
diff --git a/loc2c.c b/loc2c.c
index 5d6b549..0716c7d 100644
--- a/loc2c.c
+++ b/loc2c.c
@@ -2071,7 +2071,8 @@ emit_loc_address (FILE *out, struct location *loc, unsigned int indent,
    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 location *loc, unsigned int indent,
     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 location *loc, unsigned int indent,
 }
 
 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 location *loc, int indent)
       }
 
   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 location *loc, int indent)
       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 location *loc, int indent)
 
   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;
 }
 
diff --git a/loc2c.h b/loc2c.h
index becf2d8..45d9382 100644
--- a/loc2c.h
+++ b/loc2c.h
@@ -112,6 +112,7 @@ struct location *c_translate_argument (struct obstack *,
 
    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 : */


SystemTap-1.0-limit-printf-arguments.patch:
 buildrun.cxx                  |    3 +++
 testsuite/transko/varargs.stp |   10 ++++++++++
 testsuite/transok/varargs.stp |    9 +++++++++
 translate.cxx                 |    5 +++++
 4 files changed, 27 insertions(+)

--- NEW FILE SystemTap-1.0-limit-printf-arguments.patch ---
diff --git a/buildrun.cxx b/buildrun.cxx
index 100cbc4..c86a442 100644
--- a/buildrun.cxx
+++ b/buildrun.cxx
@@ -200,6 +200,9 @@ compile_pass (systemtap_session& s)
 
   // o << "CFLAGS += -fno-unit-at-a-time" << endl;
 
+  // 600 bytes should be enough for anybody
+  o << "EXTRA_CFLAGS += $(call cc-option,-Wframe-larger-than=600)" << endl;
+
   // Assumes linux 2.6 kbuild
   o << "EXTRA_CFLAGS += -Wno-unused -Werror" << endl;
   #if CHECK_POINTER_ARITH_PR5947
diff --git a/testsuite/transko/varargs.stp b/testsuite/transko/varargs.stp
new file mode 100755
index 0000000..f38309a
--- /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)
+}
diff --git a/testsuite/transok/varargs.stp b/testsuite/transok/varargs.stp
new file mode 100755
index 0000000..216166f
--- /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)
+}
diff --git a/translate.cxx b/translate.cxx
index 04a9247..c73a5bd 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -4151,6 +4151,11 @@ c_unparser::visit_print_format (print_format* e)
     {
       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;
 


SystemTap-1.0-unwind-table-size-checks.patch:
 runtime/unwind.c        |   43 ++++++++++++++++++++++++++++++++++++-------
 runtime/unwind/unwind.h |    6 ++++--
 translate.cxx           |   11 +++++++++++
 3 files changed, 51 insertions(+), 9 deletions(-)

--- NEW FILE SystemTap-1.0-unwind-table-size-checks.patch ---
diff --git a/runtime/unwind.c b/runtime/unwind.c
index 00108a3..7607770 100644
--- a/runtime/unwind.c
+++ b/runtime/unwind.c
@@ -88,7 +88,7 @@ static sleb128_t get_sleb128(const u8 **pcur, const u8 *end)
 
 /* 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 *fde, void *unwind_data,
 	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 u8 **pLoc, const void *end, signed ptrTy
 	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 *cie)
 		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 item_location where, uleb128_t value, s
 	}
 }
 
+/* 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, const u8 *end, unsigned long targetLoc, s
 	} 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_frame_info *frame,
 
 	/* 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 != &not_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_frame_info *frame,
 	    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 == &not_fde || (ptrType = fde_pointer_type(cie)) < 0)
+			if (cie == NULL || cie == &not_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_frame_info *frame,
 	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_frame_info *frame,
 	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);
diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h
index 285a3a3..023ea60 100644
--- a/runtime/unwind/unwind.h
+++ b/runtime/unwind/unwind.h
@@ -143,8 +143,10 @@ static unsigned long read_pointer(const u8 **pLoc,
                                   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 */
diff --git a/translate.cxx b/translate.cxx
index bc5d615..9d456bc 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -29,6 +29,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;
@@ -4785,6 +4790,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";
@@ -4802,6 +4810,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";


Index: systemtap.spec
===================================================================
RCS file: /cvs/pkgs/rpms/systemtap/F-11/systemtap.spec,v
retrieving revision 1.51
retrieving revision 1.52
diff -u -p -r1.51 -r1.52
--- systemtap.spec	23 Sep 2009 02:26:03 -0000	1.51
+++ systemtap.spec	21 Oct 2009 15:52:42 -0000	1.52
@@ -9,7 +9,7 @@
 
 Name: systemtap
 Version: 1.0
-Release: 1%{?dist}
+Release: 2%{?dist}
 # for version, see also configure.ac
 Summary: Instrumentation System
 Group: Development/System
@@ -61,6 +61,11 @@ BuildRequires: xmlto /usr/share/xmlto/fo
 BuildRequires: gtkmm24-devel >= 2.8
 %endif
 
+# Fix three --unprivileged DOS issues (CVE-2009-2911)
+Patch10: SystemTap-1.0-limit-printf-arguments.patch
+Patch11: SystemTap-1.0-limit-dwarf-expression-stack-size.patch
+Patch12: SystemTap-1.0-unwind-table-size-checks.patch
+
 %description
 SystemTap is an instrumentation system for systems running Linux 2.6.
 Developers can write instrumentation to collect data on the operation
@@ -164,6 +169,11 @@ find . \( -name configure -o -name confi
 cd ..
 %endif
 
+# Fix three --unprivileged DOS issues (CVE-2009-2911)
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+
 %build
 
 %if %{with_bundled_elfutils}
@@ -387,6 +397,9 @@ exit 0
 
 
 %changelog
+* Wed Oct 21 2009 Josh Stone <jistone at redhat.com> - 1.0-2
+- Fix three --unprivileged DOS issues (CVE-2009-2911)
+
 * Tue Sep 22 2009 Josh Stone <jistone at redhat.com> - 1.0-1
 - Upstream release.
 




More information about the fedora-extras-commits mailing list