[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gcc-patches
Subject:    [RFC] [PATCH] Implement -ffortify for C/C++
From:       Dirk Mueller <dmueller () suse ! de>
Date:       2007-01-30 15:43:05
Message-ID: 200701301643.12230.dmueller () suse ! de
[Download RAW message or body]

Hi, 

this patch adds a command line switch -ffortify=1,2 that works simliar to the 
glibc define -D_FORTIFY_SOURCE=1,2. The main difference to the glibc 
implementation is that it also works for C++ code. The glibc implementation 
uses #define's that replace certain string and memory related functions (e.g. 
strcpy) with the strcpy_chk variant. This does not work with C++ due to 
namespace issues and is therefore disabled alltogether for this language. The 
gcc implementation does not suffer from this issue and can also fortify 
namespaced calls, e.g. calls to "std::strcpy" correctly. 

The patch is so far bootstrapped for c,c++ and fortran and tested on various
testcases and real world code. regression test is unaffected since it is not 
enabled by default. A regression test run with unconditionally enabled 
ffortify leads to some failures, which I believe are related to testcases not 
expecting the rewrite (investigation still in progress). 

Comments?

Thanks,
Dirk

2007-01-27  Dirk Mueller  <dmueller@suse.de>

        * doc/invoke.texi (-ffortify): Document.
        * common.opt (ffortify): Define.
        c-common.c (build_bos_call, fortify_mem_builtin_fn, 
        fortify_printf_builtin_fn): New.
        (resolve_overloaded_builtin): Call fortify_mem_builtin_fn
        and fortify_printf_builtin_fn if -ffortify is set to
        substitute directly into *_chk variants of the fortify builtins.

        * cp/semantics.c (finish_call_expr): Move call to
        resolve_overloaded_builtin ..
	* cp/call.c (build_new_function_call): to after overload
        resolution to be able to fortify even std:: variants of builtins.


["fortify-2.diff" (text/x-diff)]

2007-01-27  Dirk Mueller  <dmueller@suse.de>

        * doc/invoke.texi (-ffortify): Document.
        * common.opt (ffortify): Define.
        c-common.c (build_bos_call, fortify_mem_builtin_fn, 
        fortify_printf_builtin_fn): New.
        (resolve_overloaded_builtin): Call fortify_mem_builtin_fn
        and fortify_printf_builtin_fn if -ffortify is set to
        substitute directly into *_chk variants of the fortify builtins.

        * cp/semantics.c (finish_call_expr, build_new_function_call):
        move call to resolve_overloaded_builtin to after overload
        resolution to be able to fortify even std:: variants of builtins.

--- doc/invoke.texi	(revision 121080)
+++ doc/invoke.texi	(working copy)
@@ -306,7 +306,7 @@ Objective-C and Objective-C++ Dialects}.
 @xref{Optimize Options,,Options that Control Optimization}.
 @gccoptlist{-falign-functions=@var{n}  -falign-jumps=@var{n} @gol
 -falign-labels=@var{n}  -falign-loops=@var{n}  @gol
--fbounds-check -fmudflap -fmudflapth -fmudflapir @gol
+-fbounds-check -ffortify=@var{n} -fmudflap -fmudflapth -fmudflapir @gol
 -fbranch-probabilities -fprofile-values -fvpt -fbranch-target-load-optimize @gol
 -fbranch-target-load-optimize2 -fbtr-bb-exclusive @gol
 -fcaller-saves  -fcprop-registers  -fcse-follow-jumps @gol
@@ -4880,6 +4892,18 @@ indices used to access arrays are within
 currently only supported by the Java and Fortran front-ends, where
 this option defaults to true and false respectively.
 
+@item -ffortify
+@itemx -ffortify=@var{n}
+Instrument a set of string, memory and printf related functions into their
+_chk variants. @var{n} controls the behaviour in detail. A fortify level
+of 1 will produce compile warnings, but expand the _chk variants
+back into the non-checking equivalent, and therefore not introduce any
+run-time overhead in the generated code. A level of 2 will emit calls
+to the _chk variants if the overflow checking can only be done during
+runtime. Higher fortify levels are passed down to the printf related
+check functions and might control further fortify levels. A level of 0
+disables the instrumentation.
+
 @item -fmudflap -fmudflapth -fmudflapir
 @opindex fmudflap
 @opindex fmudflapth
--- common.opt	(revision 121080)
+++ common.opt	(working copy)
@@ -296,6 +298,10 @@ fbounds-check
 Common Report Var(flag_bounds_check)
 Generate code to check bounds before indexing arrays
 
+ffortify=
+Common Report Joined UInteger Var(flag_fortify)
+Fortify all calls to string functions into their _chk variants
+
 fbranch-count-reg
 Common Report Var(flag_branch_on_count_reg) Init(1)
 Replace add, compare, branch with branch on count register
--- c-common.c	(revision 121080)
+++ c-common.c	(working copy)
@@ -6551,6 +6559,118 @@ sync_resolve_return (tree params, tree r
   return convert (ptype, result);
 }
 
+
+/* A helper to construct a function call to __builtin_object_size
+   with ARG0 and ARG1.  */
+
+static tree
+build_bos_call (tree arg0, tree arg1)
+{
+  tree fn, a;
+
+  a = build_tree_list (NULL_TREE, arg0);
+  TREE_CHAIN (a) = build_tree_list (NULL_TREE, arg1);
+
+  fn = build_function_call (implicit_built_in_decls[BUILT_IN_OBJECT_SIZE], a);
+
+  if (fn == error_mark_node)
+    return NULL_TREE;
+
+  return fn;
+}
+
+
+/* Fortify the str* or mem* related builtin FUNCTION into the appropriate
+   _chk variant given by FCODE, by constructing an additional parameter
+   calling __builtin_object_size with the parameter BOS on the first argument
+   of PARAMS. If anything fails, return NULL_TREE.  */
+
+
+static tree
+fortify_mem_builtin_fn(enum built_in_function fcode, int bos, tree function, tree params)
+{
+  if (TREE_VALUE (params))
+    {
+      tree a,fn;
+
+      fn = build_bos_call (TREE_VALUE (params),
+			   bos && flag_fortify > 1 ? integer_one_node : integer_zero_node);
+
+      if (!fn)
+	return NULL_TREE;
+
+      a = built_in_decls[fcode];
+      if (a)
+        {
+	  /* Append to params.  */
+	  TREE_CHAIN (tree_last (params)) = build_tree_list (NULL_TREE, fn);
+	  fn = build_function_call (a, params);
+	  if (fn == error_mark_node)
+	    return NULL_TREE;
+
+	  /* Set back the function name to give nicer diagnostic. */
+          if (DECL_P (TREE_OPERAND (TREE_OPERAND (fn, 0), 0)))
+            DECL_NAME (TREE_OPERAND (TREE_OPERAND (fn, 0), 0)) = DECL_NAME (function);
+	  return fn;
+	}
+    }
+
+  return NULL_TREE;
+}
+
+
+/* Fortify the *printf related builtin FUNCTION by rewriting it into
+   the _chk variant and adding the fortify flag to the PARAMS
+   as determined by the fortify level.  */
+
+static tree
+fortify_printf_builtin_fn (int flag_num, bool append_bos,
+    enum built_in_function fcode, tree function, tree params)
+{
+  tree orig_params = params;
+  tree fn = NULL_TREE;
+  tree flag;
+
+  flag = build_tree_list (NULL_TREE, build_int_cst (NULL_TREE, flag_fortify - 1));
+
+  if (append_bos)
+    {
+      tree bos_fn = build_bos_call (TREE_VALUE (orig_params), flag_fortify > 1
+	                                                      ? integer_one_node :
+                                                              integer_zero_node);
+      TREE_CHAIN (flag) = build_tree_list (NULL_TREE, bos_fn);
+    }
+
+  if (flag_num == 0)
+    {
+      TREE_CHAIN (tree_last (flag)) = orig_params;
+      orig_params = flag;
+    }
+  else
+    {
+      tree h;
+
+      while (--flag_num && TREE_CHAIN (params))
+        params = TREE_CHAIN (params);
+
+      h = TREE_CHAIN (params);
+      TREE_CHAIN (params) = flag;
+      TREE_CHAIN (tree_last (params)) = h;
+    }
+
+  if (built_in_decls[fcode])
+    fn = build_function_call (built_in_decls[fcode], orig_params);
+
+  if (fn == error_mark_node)
+    return NULL_TREE;
+
+  /* Set back the function name to give nicer diagnostic. */
+  if (DECL_P (TREE_OPERAND (TREE_OPERAND (fn, 0), 0)))
+    DECL_NAME (TREE_OPERAND (TREE_OPERAND (fn, 0), 0)) = DECL_NAME (function);
+
+  return fn;
+}
+
 /* Some builtin functions are placeholders for other expressions.  This
    function should be called immediately after parsing the call expression
    before surrounding code has committed to the type of the expression.
@@ -6578,6 +6698,49 @@ resolve_overloaded_builtin (tree functio
     }
 
   /* Handle BUILT_IN_NORMAL here.  */
+
+  if (flag_fortify)
+    {
+      switch (orig_code)
+        {
+        case BUILT_IN_MEMCPY:
+          return fortify_mem_builtin_fn (BUILT_IN_MEMCPY_CHK, 0, function, params);
+        case BUILT_IN_MEMPCPY:
+          return fortify_mem_builtin_fn (BUILT_IN_MEMPCPY_CHK, 0, function, params);
+        case BUILT_IN_MEMMOVE:
+          return fortify_mem_builtin_fn (BUILT_IN_MEMMOVE_CHK, 0, function, params);
+        case BUILT_IN_MEMSET:
+          return fortify_mem_builtin_fn (BUILT_IN_MEMSET_CHK, 0, function, params);
+        case BUILT_IN_STRCAT:
+          return fortify_mem_builtin_fn (BUILT_IN_STRCAT_CHK, 1, function, params);
+        case BUILT_IN_STPCPY:
+          return fortify_mem_builtin_fn (BUILT_IN_STPCPY_CHK, 1, function, params);
+        case BUILT_IN_STRCPY:
+          return fortify_mem_builtin_fn (BUILT_IN_STRCPY_CHK, 1, function, params);
+        case BUILT_IN_STRNCPY:
+          return fortify_mem_builtin_fn (BUILT_IN_STRNCPY_CHK, 1, function, params);
+        case BUILT_IN_STRNCAT:
+          return fortify_mem_builtin_fn (BUILT_IN_STRNCAT_CHK, 1, function, params);
+        case BUILT_IN_PRINTF:
+          return fortify_printf_builtin_fn (0, false, BUILT_IN_PRINTF_CHK, function, params);
+        case BUILT_IN_VPRINTF:
+          return fortify_printf_builtin_fn (0, false, BUILT_IN_VPRINTF_CHK, function, params);
+        case BUILT_IN_FPRINTF:
+          return fortify_printf_builtin_fn (1, false, BUILT_IN_FPRINTF_CHK, function, params);
+        case BUILT_IN_VFPRINTF:
+          return fortify_printf_builtin_fn (1, false, BUILT_IN_VFPRINTF_CHK, function, params);
+        case BUILT_IN_SPRINTF:
+          return fortify_printf_builtin_fn (1, true, BUILT_IN_SPRINTF_CHK, function, params);
+        case BUILT_IN_VSPRINTF:
+          return fortify_printf_builtin_fn (1, true, BUILT_IN_VSPRINTF_CHK, function, params);
+        case BUILT_IN_SNPRINTF:
+          return fortify_printf_builtin_fn (2, true, BUILT_IN_SNPRINTF_CHK, function, params);
+        case BUILT_IN_VSNPRINTF:
+          return fortify_printf_builtin_fn (2, true, BUILT_IN_VSNPRINTF_CHK, function, params);
+        default: break;
+        }
+    }
+
   switch (orig_code)
     {
     case BUILT_IN_FETCH_AND_ADD_N:
--- cp/call.c	(revision 121080)
+++ cp/call.c	(working copy)
@@ -2835,7 +2835,17 @@ build_new_function_call (tree fn, tree a
       result = error_mark_node;
     }
   else
-    result = build_over_call (cand, LOOKUP_NORMAL);
+    {
+      result = NULL_TREE;
+      /* If the function is an overloaded builtin, resolve it.  */
+      if (TREE_CODE (cand->fn) == FUNCTION_DECL
+	  && (DECL_BUILT_IN_CLASS (cand->fn) == BUILT_IN_NORMAL
+	      || DECL_BUILT_IN_CLASS (cand->fn) == BUILT_IN_MD))
+	result = resolve_overloaded_builtin (cand->fn, cand->args);
+
+      if (!result)
+	result = build_over_call (cand, LOOKUP_NORMAL);
+    }
 
   /* Free all the conversions we allocated.  */
   obstack_free (&conversion_obstack, p);
--- cp/semantics.c	(revision 121080)
+++ cp/semantics.c	(working copy)
@@ -1857,15 +1863,7 @@ finish_call_expr (tree fn, tree args, bo
     }
   else if (is_overloaded_fn (fn))
     {
-      /* If the function is an overloaded builtin, resolve it.  */
-      if (TREE_CODE (fn) == FUNCTION_DECL
-	  && (DECL_BUILT_IN_CLASS (fn) == BUILT_IN_NORMAL
-	      || DECL_BUILT_IN_CLASS (fn) == BUILT_IN_MD))
-	result = resolve_overloaded_builtin (fn, args);
-
-      if (!result)
-	/* A call to a namespace-scope function.  */
-	result = build_new_function_call (fn, args, koenig_p);
+      result = build_new_function_call (fn, args, koenig_p);
     }
   else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR)
     {


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic