[1b/6] Add __attribute__((untrusted))

Message ID 20211113203732.2098220-3-dmalcolm@redhat.com
State New
Headers
Series [1b/6] Add __attribute__((untrusted)) |

Commit Message

David Malcolm Nov. 13, 2021, 8:37 p.m. UTC
  This patch adds a new:

  __attribute__((untrusted))

for use by the C front-end, intended for use by the Linux kernel for
use with "__user", but which could be used by other operating system
kernels, and potentialy by other projects.

Known issues:
- at least one TODO in handle_untrusted_attribute
- should it be permitted to dereference an untrusted pointer?  The patch
  currently allows this

gcc/c-family/ChangeLog:
	* c-attribs.c (c_common_attribute_table): Add "untrusted".
	(build_untrusted_type): New.
	(handle_untrusted_attribute): New.
	* c-pretty-print.c (pp_c_cv_qualifiers): Handle
	TYPE_QUAL_UNTRUSTED.

gcc/c/ChangeLog:
	* c-typeck.c (convert_for_assignment): Complain if the trust
	levels vary when assigning a non-NULL pointer.

gcc/ChangeLog:
	* doc/extend.texi (Common Type Attributes): Add "untrusted".
	* print-tree.c (print_node): Handle TYPE_UNTRUSTED.
	* tree-core.h (enum cv_qualifier): Add TYPE_QUAL_UNTRUSTED.
	(struct tree_type_common): Assign one of the spare bits to a new
	"untrusted_flag".
	* tree.c (set_type_quals): Handle TYPE_QUAL_UNTRUSTED.
	* tree.h (TYPE_QUALS): Likewise.
	(TYPE_QUALS_NO_ADDR_SPACE): Likewise.
	(TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): Likewise.

gcc/testsuite/ChangeLog:
	* c-c++-common/attr-untrusted-1.c: New test.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/c-family/c-attribs.c                      |  59 +++++++
 gcc/c-family/c-pretty-print.c                 |   2 +
 gcc/c/c-typeck.c                              |  64 +++++++
 gcc/doc/extend.texi                           |  25 +++
 gcc/print-tree.c                              |   3 +
 gcc/testsuite/c-c++-common/attr-untrusted-1.c | 165 ++++++++++++++++++
 gcc/tree-core.h                               |   6 +-
 gcc/tree.c                                    |   1 +
 gcc/tree.h                                    |  11 +-
 9 files changed, 332 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/attr-untrusted-1.c
  

Comments

Martin Sebor Dec. 9, 2021, 10:54 p.m. UTC | #1
On 11/13/21 1:37 PM, David Malcolm via Gcc-patches wrote:
> This patch adds a new:
> 
>    __attribute__((untrusted))
> 
> for use by the C front-end, intended for use by the Linux kernel for
> use with "__user", but which could be used by other operating system
> kernels, and potentialy by other projects.

It looks like untrusted is a type attribute (rather than one
that applies to variables and/or function return values or
writeable by-reference arguments).  I find that quite surprising.
  I'm used to thinking of trusted vs tainted as dynamic properties
of data so I'm having trouble deciding what to think about
the attribute applying to types.  Can you explain why it's
useful on types?

I'd expect the taint property of a type to be quickly lost as
an object of the type is passed through existing APIs (e.g.,
a char array manipulated by string functions like strchr).

(I usually look at tests to help me understand the design of
a change but I couldn't find an answer to my question in those
in the patch.)

Thanks
Martin

PS I found one paper online that discusses type-based taint
analysis in Java but not much more.  I only quickly skimmed
the paper and although it conceptually makes sense I'm still
having difficulties seeing how it would be useful in C.

> 
> Known issues:
> - at least one TODO in handle_untrusted_attribute
> - should it be permitted to dereference an untrusted pointer?  The patch
>    currently allows this
> 
> gcc/c-family/ChangeLog:
> 	* c-attribs.c (c_common_attribute_table): Add "untrusted".
> 	(build_untrusted_type): New.
> 	(handle_untrusted_attribute): New.
> 	* c-pretty-print.c (pp_c_cv_qualifiers): Handle
> 	TYPE_QUAL_UNTRUSTED.
> 
> gcc/c/ChangeLog:
> 	* c-typeck.c (convert_for_assignment): Complain if the trust
> 	levels vary when assigning a non-NULL pointer.
> 
> gcc/ChangeLog:
> 	* doc/extend.texi (Common Type Attributes): Add "untrusted".
> 	* print-tree.c (print_node): Handle TYPE_UNTRUSTED.
> 	* tree-core.h (enum cv_qualifier): Add TYPE_QUAL_UNTRUSTED.
> 	(struct tree_type_common): Assign one of the spare bits to a new
> 	"untrusted_flag".
> 	* tree.c (set_type_quals): Handle TYPE_QUAL_UNTRUSTED.
> 	* tree.h (TYPE_QUALS): Likewise.
> 	(TYPE_QUALS_NO_ADDR_SPACE): Likewise.
> 	(TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 	* c-c++-common/attr-untrusted-1.c: New test.
> 
> Signed-off-by: David Malcolm <dmalcolm@redhat.com>
> ---
>   gcc/c-family/c-attribs.c                      |  59 +++++++
>   gcc/c-family/c-pretty-print.c                 |   2 +
>   gcc/c/c-typeck.c                              |  64 +++++++
>   gcc/doc/extend.texi                           |  25 +++
>   gcc/print-tree.c                              |   3 +
>   gcc/testsuite/c-c++-common/attr-untrusted-1.c | 165 ++++++++++++++++++
>   gcc/tree-core.h                               |   6 +-
>   gcc/tree.c                                    |   1 +
>   gcc/tree.h                                    |  11 +-
>   9 files changed, 332 insertions(+), 4 deletions(-)
>   create mode 100644 gcc/testsuite/c-c++-common/attr-untrusted-1.c
> 
> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> index 007b928c54b..100c2dabab2 100644
> --- a/gcc/c-family/c-attribs.c
> +++ b/gcc/c-family/c-attribs.c
> @@ -136,6 +136,7 @@ static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
>   						 bool *);
>   static tree handle_access_attribute (tree *, tree, tree, int, bool *);
>   
> +static tree handle_untrusted_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
>   static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
> @@ -536,6 +537,8 @@ const struct attribute_spec c_common_attribute_table[] =
>   			      handle_special_var_sec_attribute, attr_section_exclusions },
>     { "access",		      1, 3, false, true, true, false,
>   			      handle_access_attribute, NULL },
> +  { "untrusted",	      0, 0, false,  true, false, true,
> +			      handle_untrusted_attribute, NULL },
>     /* Attributes used by Objective-C.  */
>     { "NSObject",		      0, 0, true, false, false, false,
>   			      handle_nsobject_attribute, NULL },
> @@ -5224,6 +5227,62 @@ build_attr_access_from_parms (tree parms, bool skip_voidptr)
>     return build_tree_list (name, attrargs);
>   }
>   
> +/* Build (or reuse) a type based on BASE_TYPE, but with
> +   TYPE_QUAL_UNTRUSTED.  */
> +
> +static tree
> +build_untrusted_type (tree base_type)
> +{
> +  int base_type_quals = TYPE_QUALS (base_type);
> +  return build_qualified_type (base_type,
> +			       base_type_quals | TYPE_QUAL_UNTRUSTED);
> +}
> +
> +/* Handle an "untrusted" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_untrusted_attribute (tree *node, tree ARG_UNUSED (name),
> +			    tree ARG_UNUSED (args), int ARG_UNUSED (flags),
> +			    bool *no_add_attrs)
> +{
> +  if (TREE_CODE (*node) == POINTER_TYPE)
> +    {
> +      tree base_type = TREE_TYPE (*node);
> +      tree untrusted_base_type = build_untrusted_type (base_type);
> +      *node = build_pointer_type (untrusted_base_type);
> +      *no_add_attrs = true; /* OK */
> +      return NULL_TREE;
> +    }
> +  else if (TREE_CODE (*node) == FUNCTION_TYPE)
> +    {
> +      tree return_type = TREE_TYPE (*node);
> +      if (TREE_CODE (return_type) == POINTER_TYPE)
> +	{
> +	  tree base_type = TREE_TYPE (return_type);
> +	  tree untrusted_base_type = build_untrusted_type (base_type);
> +	  tree untrusted_return_type = build_pointer_type (untrusted_base_type);
> +	  tree fn_type = build_function_type (untrusted_return_type,
> +					      TYPE_ARG_TYPES (*node));
> +	  *node = fn_type;
> +	  *no_add_attrs = true; /* OK */
> +	  return NULL_TREE;
> +	}
> +      else
> +	{
> +	  gcc_unreachable (); // TODO
> +	}
> +    }
> +  else
> +    {
> +      tree base_type = *node;
> +      tree untrusted_base_type = build_untrusted_type (base_type);
> +      *node = untrusted_base_type;
> +      *no_add_attrs = true; /* OK */
> +      return NULL_TREE;
> +    }
> +}
> +
>   /* Handle a "nothrow" attribute; arguments as in
>      struct attribute_spec.handler.  */
>   
> diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
> index a987da46d6d..120e1e6d167 100644
> --- a/gcc/c-family/c-pretty-print.c
> +++ b/gcc/c-family/c-pretty-print.c
> @@ -191,6 +191,8 @@ pp_c_cv_qualifiers (c_pretty_printer *pp, int qualifiers, bool func_type)
>     if (qualifiers & TYPE_QUAL_RESTRICT)
>       pp_c_ws_string (pp, (flag_isoc99 && !c_dialect_cxx ()
>   			 ? "restrict" : "__restrict__"));
> +  if (qualifiers & TYPE_QUAL_UNTRUSTED)
> +    pp_c_ws_string (pp, "__attribute__((untrusted))");
>   }
>   
>   /* Pretty-print T using the type-cast notation '( type-name )'.  */
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 782414f8c8c..44de82b99ba 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -7284,6 +7284,70 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
>   	  return error_mark_node;
>   	}
>   
> +      /* Untrusted vs trusted pointers, but allowing NULL to be used
> +	 for everything.  */
> +      if (TYPE_UNTRUSTED (ttl) != TYPE_UNTRUSTED (ttr)
> +	  && !null_pointer_constant_p (rhs))
> +	{
> +	  auto_diagnostic_group d;
> +	  bool diagnosed = true;
> +	  switch (errtype)
> +	    {
> +	    case ic_argpass:
> +	      {
> +		const char msg[] = G_("passing argument %d of %qE from "
> +				      "pointer with different trust level");
> +		if (warnopt)
> +		  diagnosed
> +		    = warning_at (expr_loc, warnopt, msg, parmnum, rname);
> +		else
> +		  error_at (expr_loc, msg, parmnum, rname);
> +	      break;
> +	      }
> +	    case ic_assign:
> +	      {
> +		const char msg[] = G_("assignment from pointer with "
> +				      "different trust level");
> +		if (warnopt)
> +		  warning_at (location, warnopt, msg);
> +		else
> +		  error_at (location, msg);
> +		break;
> +	      }
> +	    case ic_init:
> +	      {
> +		const char msg[] = G_("initialization from pointer with "
> +				      "different trust level");
> +		if (warnopt)
> +		  warning_at (location, warnopt, msg);
> +		else
> +		  error_at (location, msg);
> +		break;
> +	      }
> +	    case ic_return:
> +	      {
> +		const char msg[] = G_("return from pointer with "
> +				      "different trust level");
> +		if (warnopt)
> +		  warning_at (location, warnopt, msg);
> +		else
> +		  error_at (location, msg);
> +		break;
> +	      }
> +	    default:
> +	      gcc_unreachable ();
> +	    }
> +	  if (diagnosed)
> +	    {
> +	      if (errtype == ic_argpass)
> +		inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	      else
> +		inform (location, "expected %qT but pointer is of type %qT",
> +			type, rhstype);
> +	    }
> +	  return error_mark_node;
> +	}
> +
>         /* Check if the right-hand side has a format attribute but the
>   	 left-hand side doesn't.  */
>         if (warn_suggest_attribute_format
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 6e6c580e329..e9f47519df2 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -8770,6 +8770,31 @@ pid_t wait (wait_status_ptr_t p)
>   @}
>   @end smallexample
>   
> +@item untrusted
> +@cindex @code{untrusted} type attribute
> +Types marked with this attribute are treated as being ``untrusted'' -
> +values should be treated as under attacker control.
> +
> +The C front end will issue an error diagnostic on attempts to assign
> +pointer values between untrusted and trusted pointer types without
> +an explicit cast.
> +
> +For example, when implementing an operating system kernel, one
> +might write
> +
> +@smallexample
> +#define __kernel
> +#define __user    __attribute__ ((untrusted))
> +void __kernel *p_kernel;
> +void __user *p_user;
> +
> +/* With the above, the following assignment should be diagnosed as an error.  */
> +p_user = p_kernel;
> +@end smallexample
> +
> +The NULL pointer is treated as being usable with both trusted and
> +untrusted pointers.
> +
>   @item unused
>   @cindex @code{unused} type attribute
>   When attached to a type (including a @code{union} or a @code{struct}),
> diff --git a/gcc/print-tree.c b/gcc/print-tree.c
> index d1fbd044c27..e5123807521 100644
> --- a/gcc/print-tree.c
> +++ b/gcc/print-tree.c
> @@ -640,6 +640,9 @@ print_node (FILE *file, const char *prefix, tree node, int indent,
>         if (TYPE_RESTRICT (node))
>   	fputs (" restrict", file);
>   
> +      if (TYPE_UNTRUSTED (node))
> +	fputs (" untrusted", file);
> +
>         if (TYPE_LANG_FLAG_0 (node))
>   	fputs (" type_0", file);
>         if (TYPE_LANG_FLAG_1 (node))
> diff --git a/gcc/testsuite/c-c++-common/attr-untrusted-1.c b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
> new file mode 100644
> index 00000000000..84a217fc59f
> --- /dev/null
> +++ b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
> @@ -0,0 +1,165 @@
> +#define __kernel
> +#define __user __attribute__((untrusted))
> +#define __iomem
> +#define __percpu
> +#define __rcu
> +
> +void *p;
> +void __kernel *p_kernel;
> +void __user *p_user;
> +void __iomem *p_iomem;
> +void __percpu *p_percpu;
> +void __rcu *p_rcu;
> +
> +#define NULL ((void *)0)
> +
> +extern void accepts_p (void *); /* { dg-message "24: expected 'void \\*' but argument is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } } */
> +/* { dg-message "24:  initializing argument 1 of 'void accepts_p\\(void\\*\\)'" "" { target c++ } .-1 } */
> +extern void accepts_p_kernel (void __kernel *);
> +extern void accepts_p_user (void __user *);
> +
> +void test_argpass_to_p (void)
> +{
> +  accepts_p (p);
> +  accepts_p (p_kernel);
> +  accepts_p (p_user); /* { dg-error "passing argument 1 of 'accepts_p' from pointer with different trust level" "" { target c } } */
> +  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-1 } */
> +}
> +
> +void test_init_p (void)
> +{
> +  void *local_p_1 = p;
> +  void *local_p_2 = p_kernel;
> +  void *local_p_3 = p_user; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +}
> +
> +void test_init_p_kernel (void)
> +{
> +  void __kernel *local_p_1 = p;
> +  void __kernel *local_p_2 = p_kernel;
> +  void __kernel *local_p_3 = p_user; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +}
> +
> +void test_init_p_user (void)
> +{
> +  void __user *local_p_1 = p; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +  void __user *local_p_2 = p_kernel; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +  void __user *local_p_3 = p_user;
> +  void __user *local_p_4 = NULL;
> +}
> +
> +void test_assign_to_p (void)
> +{
> +  p = p;
> +  p = p_kernel;
> +  p = p_user; /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +  // etc
> +}
> +
> +void test_assign_to_p_kernel (void)
> +{
> +  p_kernel = p;
> +  p_kernel = p_kernel;
> +  p_kernel = p_user; /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +  // etc
> +}
> +
> +void test_assign_to_p_user (void)
> +{
> +  p_user = p;  /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +  p_user = p_kernel;  /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +  p_user = p_user;
> +  p_user = NULL;
> +  // etc
> +}
> +
> +void *test_return_p (int i)
> +{
> +  switch (i)
> +    {
> +    default:
> +    case 0:
> +      return p;
> +    case 1:
> +      return p_kernel;
> +    case 2:
> +      return p_user; /* { dg-error "return from pointer with different trust level" "" { target c } } */
> +      /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +      /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +    }
> +}
> +
> +void __kernel *test_return_p_kernel (int i)
> +{
> +  switch (i)
> +    {
> +    default:
> +    case 0:
> +      return p;
> +    case 1:
> +      return p_kernel;
> +    case 2:
> +      return p_user; /* { dg-error "return from pointer with different trust level" "" { target c } } */
> +      /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
> +      /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
> +    }
> +}
> +
> +void __user *
> +test_return_p_user (int i)
> +{
> +  switch (i)
> +    {
> +    default:
> +    case 0:
> +      return p; /* { dg-error "return from pointer with different trust level" "" { target c } } */
> +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +    case 1:
> +      return p_kernel; /* { dg-error "return from pointer with different trust level" "" { target c } } */
> +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> +    case 2:
> +      return p_user;
> +    case 3:
> +      return NULL;
> +    }
> +}
> +
> +void test_cast_k_to_u (void)
> +{
> +  p_user = (void __user *)p_kernel;
> +}
> +
> +void test_cast_u_to_k (void)
> +{
> +  p_kernel = (void __kernel *)p_user;
> +}
> +
> +int test_deref_read (int __user *p)
> +{
> +  return *p; // FIXME: should this be allowed directly?
> +}
> +
> +void test_deref_write (int __user *p, int i)
> +{
> +  *p = i; // FIXME: should this be allowed directly?
> +}
> +
> +typedef struct foo { int i; } __user *foo_ptr_t;
> +
> +void __user *
> +test_pass_through (void __user *ptr)
> +{
> +  return ptr;
> +}
> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> index 8ab119dc9a2..35a7f50c06c 100644
> --- a/gcc/tree-core.h
> +++ b/gcc/tree-core.h
> @@ -604,7 +604,8 @@ enum cv_qualifier {
>     TYPE_QUAL_CONST    = 0x1,
>     TYPE_QUAL_VOLATILE = 0x2,
>     TYPE_QUAL_RESTRICT = 0x4,
> -  TYPE_QUAL_ATOMIC   = 0x8
> +  TYPE_QUAL_ATOMIC   = 0x8,
> +  TYPE_QUAL_UNTRUSTED = 0x10
>   };
>   
>   /* Standard named or nameless data types of the C compiler.  */
> @@ -1684,7 +1685,8 @@ struct GTY(()) tree_type_common {
>     unsigned typeless_storage : 1;
>     unsigned empty_flag : 1;
>     unsigned indivisible_p : 1;
> -  unsigned spare : 16;
> +  unsigned untrusted_flag : 1;
> +  unsigned spare : 15;
>   
>     alias_set_type alias_set;
>     tree pointer_to;
> diff --git a/gcc/tree.c b/gcc/tree.c
> index 845228a055b..3600639d985 100644
> --- a/gcc/tree.c
> +++ b/gcc/tree.c
> @@ -5379,6 +5379,7 @@ set_type_quals (tree type, int type_quals)
>     TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0;
>     TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0;
>     TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0;
> +  TYPE_UNTRUSTED (type) = (type_quals & TYPE_QUAL_UNTRUSTED) != 0;
>     TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals);
>   }
>   
> diff --git a/gcc/tree.h b/gcc/tree.h
> index f62c00bc870..caab575b210 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -2197,6 +2197,10 @@ extern tree vector_element_bits_tree (const_tree);
>      the term.  */
>   #define TYPE_RESTRICT(NODE) (TYPE_CHECK (NODE)->type_common.restrict_flag)
>   
> +/* Nonzero in a type considered "untrusted" - values should be treated as
> +   under attacker control.  */
> +#define TYPE_UNTRUSTED(NODE) (TYPE_CHECK (NODE)->type_common.untrusted_flag)
> +
>   /* If nonzero, type's name shouldn't be emitted into debug info.  */
>   #define TYPE_NAMELESS(NODE) (TYPE_CHECK (NODE)->base.u.bits.nameless_flag)
>   
> @@ -2221,6 +2225,7 @@ extern tree vector_element_bits_tree (const_tree);
>   	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
>   	  | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)		\
>   	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
> +	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)	\
>   	  | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE)))))
>   
>   /* The same as TYPE_QUALS without the address space qualifications.  */
> @@ -2228,14 +2233,16 @@ extern tree vector_element_bits_tree (const_tree);
>     ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)		\
>   	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
>   	  | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)		\
> -	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> +	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
> +	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
>   
>   /* The same as TYPE_QUALS without the address space and atomic
>      qualifications.  */
>   #define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE)		\
>     ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)		\
>   	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
> -	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> +	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
> +	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
>   
>   /* These flags are available for each language front end to use internally.  */
>   #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_0)
>
  
David Malcolm Jan. 6, 2022, 3:10 p.m. UTC | #2
On Thu, 2021-12-09 at 15:54 -0700, Martin Sebor wrote:
> On 11/13/21 1:37 PM, David Malcolm via Gcc-patches wrote:
> > This patch adds a new:
> > 
> >    __attribute__((untrusted))
> > 
> > for use by the C front-end, intended for use by the Linux kernel for
> > use with "__user", but which could be used by other operating system
> > kernels, and potentialy by other projects.
> 
> It looks like untrusted is a type attribute (rather than one
> that applies to variables and/or function return values or
> writeable by-reference arguments).  I find that quite surprising.

FWIW I initially tried implementing it on pointer types, but doing it
on the underlying type was much cleaner.

>   I'm used to thinking of trusted vs tainted as dynamic properties
> of data so I'm having trouble deciding what to think about
> the attribute applying to types.  Can you explain why it's
> useful on types?

A type system *is* a way of detecting problems involving dynamic
properties of data.  Ultimately all we have at runtime is a collection
of bits; the toolchain has the concept of types as a way to allow us to
reason about properies of those bits without requiring a full cross-TU
analysis (to try to figure out that e.g. x is, say, a 32 bit unsigned
integer), and to document these properties clearly to human readers of
the code.

I see this as working like a qualifier (rather like "const" and
"volatile"), in that an
  untrusted char *
when dereferenced gives you an
  untrusted char

The intent is to have a way of treating the values as "actively
hostile", so that code analyzers can assume the worst possible values
for such types (or more glibly, that we're dealing with data from Satan
rather than from Murphy).

Such types are also relevant to infoleaks: writing sensitive
information to an untrusted value can be detected relatively easily
with this approach, by checking the type of the value - the types
express the trust boundary

Doing this with qualifiers allows us to use the C type system to detect
these kinds of issues without having to add a full cross-TU
interprocedural analysis, and documents it to human readers of the
code.   Compare with const-correctness; we can have an analogous
"trust-correctness".

> 
> I'd expect the taint property of a type to be quickly lost as
> an object of the type is passed through existing APIs (e.g.,
> a char array manipulated by string functions like strchr).

FWIW you can't directly pass an attacker-controlled buffer to strchr:
strchr requires there to be a 0-terminator to the array; if the array's
content is untrusted then the attacker might not have 0-terminated it.

As implemented, the patch doesn't complain about this, though maybe it
should.

The main point here is to support the existing __user annotation used
by the Linux kernel, in particular, copy_from_user and copy_to_user.

> 
> (I usually look at tests to help me understand the design of
> a change but I couldn't find an answer to my question in those
> in the patch.)

The patch kit was rather unclear on this, due to the use of two
different approaches (custom address spaces vs this untrusted
attribute).  Sorry about this.

Patches 4a and 4b in the kit add test-uaccess.h (to
gcc/testsuite/gcc.dg/analyzer) which supplies "__user"; see the tests
that use "test-uaccess.h" in patch 3:
 [PATCH 3/6] analyzer: implement infoleak detection
    https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584377.html
and in patch 5:
 [PATCH 5/6] analyzer: use region::untrusted_p in taint detection
   https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584374.html

(sorry about messing up the order of the patches).

Patch 4a here:
 [PATCH 4a/6] analyzer: implement region::untrusted_p in terms of custom address spaces
   https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584371.html
implements "__user" as a custom address space, 

whereas patch 4b here:

 [PATCH 4b/6] analyzer: implement region::untrusted_p in terms of __attribute__((untrusted))
    https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584373.html

implements "__user" to be __attribute__((untrusted)).

Perhaps I should drop the custom address space versions of the patches
and post a version of the kit that simply uses the attribute?

Dave


> 
> Thanks
> Martin
> 
> PS I found one paper online that discusses type-based taint
> analysis in Java but not much more.  I only quickly skimmed
> the paper and although it conceptually makes sense I'm still
> having difficulties seeing how it would be useful in C.
> 
> > 
> > Known issues:
> > - at least one TODO in handle_untrusted_attribute
> > - should it be permitted to dereference an untrusted pointer?  The
> > patch
> >    currently allows this
> > 
> > gcc/c-family/ChangeLog:
> >         * c-attribs.c (c_common_attribute_table): Add "untrusted".
> >         (build_untrusted_type): New.
> >         (handle_untrusted_attribute): New.
> >         * c-pretty-print.c (pp_c_cv_qualifiers): Handle
> >         TYPE_QUAL_UNTRUSTED.
> > 
> > gcc/c/ChangeLog:
> >         * c-typeck.c (convert_for_assignment): Complain if the trust
> >         levels vary when assigning a non-NULL pointer.
> > 
> > gcc/ChangeLog:
> >         * doc/extend.texi (Common Type Attributes): Add "untrusted".
> >         * print-tree.c (print_node): Handle TYPE_UNTRUSTED.
> >         * tree-core.h (enum cv_qualifier): Add TYPE_QUAL_UNTRUSTED.
> >         (struct tree_type_common): Assign one of the spare bits to a
> > new
> >         "untrusted_flag".
> >         * tree.c (set_type_quals): Handle TYPE_QUAL_UNTRUSTED.
> >         * tree.h (TYPE_QUALS): Likewise.
> >         (TYPE_QUALS_NO_ADDR_SPACE): Likewise.
> >         (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): Likewise.
> > 
> > gcc/testsuite/ChangeLog:
> >         * c-c++-common/attr-untrusted-1.c: New test.
> > 
> > Signed-off-by: David Malcolm <dmalcolm@redhat.com>
> > ---
> >   gcc/c-family/c-attribs.c                      |  59 +++++++
> >   gcc/c-family/c-pretty-print.c                 |   2 +
> >   gcc/c/c-typeck.c                              |  64 +++++++
> >   gcc/doc/extend.texi                           |  25 +++
> >   gcc/print-tree.c                              |   3 +
> >   gcc/testsuite/c-c++-common/attr-untrusted-1.c | 165
> > ++++++++++++++++++
> >   gcc/tree-core.h                               |   6 +-
> >   gcc/tree.c                                    |   1 +
> >   gcc/tree.h                                    |  11 +-
> >   9 files changed, 332 insertions(+), 4 deletions(-)
> >   create mode 100644 gcc/testsuite/c-c++-common/attr-untrusted-1.c
> > 
> > diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
> > index 007b928c54b..100c2dabab2 100644
> > --- a/gcc/c-family/c-attribs.c
> > +++ b/gcc/c-family/c-attribs.c
> > @@ -136,6 +136,7 @@ static tree handle_warn_unused_result_attribute
> > (tree *, tree, tree, int,
> >                                                  bool *);
> >   static tree handle_access_attribute (tree *, tree, tree, int, bool
> > *);
> >   
> > +static tree handle_untrusted_attribute (tree *, tree, tree, int,
> > bool *);
> >   static tree handle_sentinel_attribute (tree *, tree, tree, int,
> > bool *);
> >   static tree handle_type_generic_attribute (tree *, tree, tree, int,
> > bool *);
> >   static tree handle_alloc_size_attribute (tree *, tree, tree, int,
> > bool *);
> > @@ -536,6 +537,8 @@ const struct attribute_spec
> > c_common_attribute_table[] =
> >                               handle_special_var_sec_attribute,
> > attr_section_exclusions },
> >     { "access",               1, 3, false, true, true, false,
> >                               handle_access_attribute, NULL },
> > +  { "untrusted",             0, 0, false,  true, false, true,
> > +                             handle_untrusted_attribute, NULL },
> >     /* Attributes used by Objective-C.  */
> >     { "NSObject",                     0, 0, true, false, false,
> > false,
> >                               handle_nsobject_attribute, NULL },
> > @@ -5224,6 +5227,62 @@ build_attr_access_from_parms (tree parms, bool
> > skip_voidptr)
> >     return build_tree_list (name, attrargs);
> >   }
> >   
> > +/* Build (or reuse) a type based on BASE_TYPE, but with
> > +   TYPE_QUAL_UNTRUSTED.  */
> > +
> > +static tree
> > +build_untrusted_type (tree base_type)
> > +{
> > +  int base_type_quals = TYPE_QUALS (base_type);
> > +  return build_qualified_type (base_type,
> > +                              base_type_quals |
> > TYPE_QUAL_UNTRUSTED);
> > +}
> > +
> > +/* Handle an "untrusted" attribute; arguments as in
> > +   struct attribute_spec.handler.  */
> > +
> > +static tree
> > +handle_untrusted_attribute (tree *node, tree ARG_UNUSED (name),
> > +                           tree ARG_UNUSED (args), int ARG_UNUSED
> > (flags),
> > +                           bool *no_add_attrs)
> > +{
> > +  if (TREE_CODE (*node) == POINTER_TYPE)
> > +    {
> > +      tree base_type = TREE_TYPE (*node);
> > +      tree untrusted_base_type = build_untrusted_type (base_type);
> > +      *node = build_pointer_type (untrusted_base_type);
> > +      *no_add_attrs = true; /* OK */
> > +      return NULL_TREE;
> > +    }
> > +  else if (TREE_CODE (*node) == FUNCTION_TYPE)
> > +    {
> > +      tree return_type = TREE_TYPE (*node);
> > +      if (TREE_CODE (return_type) == POINTER_TYPE)
> > +       {
> > +         tree base_type = TREE_TYPE (return_type);
> > +         tree untrusted_base_type = build_untrusted_type
> > (base_type);
> > +         tree untrusted_return_type = build_pointer_type
> > (untrusted_base_type);
> > +         tree fn_type = build_function_type (untrusted_return_type,
> > +                                             TYPE_ARG_TYPES
> > (*node));
> > +         *node = fn_type;
> > +         *no_add_attrs = true; /* OK */
> > +         return NULL_TREE;
> > +       }
> > +      else
> > +       {
> > +         gcc_unreachable (); // TODO
> > +       }
> > +    }
> > +  else
> > +    {
> > +      tree base_type = *node;
> > +      tree untrusted_base_type = build_untrusted_type (base_type);
> > +      *node = untrusted_base_type;
> > +      *no_add_attrs = true; /* OK */
> > +      return NULL_TREE;
> > +    }
> > +}
> > +
> >   /* Handle a "nothrow" attribute; arguments as in
> >      struct attribute_spec.handler.  */
> >   
> > diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-
> > print.c
> > index a987da46d6d..120e1e6d167 100644
> > --- a/gcc/c-family/c-pretty-print.c
> > +++ b/gcc/c-family/c-pretty-print.c
> > @@ -191,6 +191,8 @@ pp_c_cv_qualifiers (c_pretty_printer *pp, int
> > qualifiers, bool func_type)
> >     if (qualifiers & TYPE_QUAL_RESTRICT)
> >       pp_c_ws_string (pp, (flag_isoc99 && !c_dialect_cxx ()
> >                          ? "restrict" : "__restrict__"));
> > +  if (qualifiers & TYPE_QUAL_UNTRUSTED)
> > +    pp_c_ws_string (pp, "__attribute__((untrusted))");
> >   }
> >   
> >   /* Pretty-print T using the type-cast notation '( type-name )'.  */
> > diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> > index 782414f8c8c..44de82b99ba 100644
> > --- a/gcc/c/c-typeck.c
> > +++ b/gcc/c/c-typeck.c
> > @@ -7284,6 +7284,70 @@ convert_for_assignment (location_t location,
> > location_t expr_loc, tree type,
> >           return error_mark_node;
> >         }
> >   
> > +      /* Untrusted vs trusted pointers, but allowing NULL to be used
> > +        for everything.  */
> > +      if (TYPE_UNTRUSTED (ttl) != TYPE_UNTRUSTED (ttr)
> > +         && !null_pointer_constant_p (rhs))
> > +       {
> > +         auto_diagnostic_group d;
> > +         bool diagnosed = true;
> > +         switch (errtype)
> > +           {
> > +           case ic_argpass:
> > +             {
> > +               const char msg[] = G_("passing argument %d of %qE
> > from "
> > +                                     "pointer with different trust
> > level");
> > +               if (warnopt)
> > +                 diagnosed
> > +                   = warning_at (expr_loc, warnopt, msg, parmnum,
> > rname);
> > +               else
> > +                 error_at (expr_loc, msg, parmnum, rname);
> > +             break;
> > +             }
> > +           case ic_assign:
> > +             {
> > +               const char msg[] = G_("assignment from pointer with "
> > +                                     "different trust level");
> > +               if (warnopt)
> > +                 warning_at (location, warnopt, msg);
> > +               else
> > +                 error_at (location, msg);
> > +               break;
> > +             }
> > +           case ic_init:
> > +             {
> > +               const char msg[] = G_("initialization from pointer
> > with "
> > +                                     "different trust level");
> > +               if (warnopt)
> > +                 warning_at (location, warnopt, msg);
> > +               else
> > +                 error_at (location, msg);
> > +               break;
> > +             }
> > +           case ic_return:
> > +             {
> > +               const char msg[] = G_("return from pointer with "
> > +                                     "different trust level");
> > +               if (warnopt)
> > +                 warning_at (location, warnopt, msg);
> > +               else
> > +                 error_at (location, msg);
> > +               break;
> > +             }
> > +           default:
> > +             gcc_unreachable ();
> > +           }
> > +         if (diagnosed)
> > +           {
> > +             if (errtype == ic_argpass)
> > +               inform_for_arg (fundecl, expr_loc, parmnum, type,
> > rhstype);
> > +             else
> > +               inform (location, "expected %qT but pointer is of
> > type %qT",
> > +                       type, rhstype);
> > +           }
> > +         return error_mark_node;
> > +       }
> > +
> >         /* Check if the right-hand side has a format attribute but
> > the
> >          left-hand side doesn't.  */
> >         if (warn_suggest_attribute_format
> > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > index 6e6c580e329..e9f47519df2 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -8770,6 +8770,31 @@ pid_t wait (wait_status_ptr_t p)
> >   @}
> >   @end smallexample
> >   
> > +@item untrusted
> > +@cindex @code{untrusted} type attribute
> > +Types marked with this attribute are treated as being ``untrusted''
> > -
> > +values should be treated as under attacker control.
> > +
> > +The C front end will issue an error diagnostic on attempts to assign
> > +pointer values between untrusted and trusted pointer types without
> > +an explicit cast.
> > +
> > +For example, when implementing an operating system kernel, one
> > +might write
> > +
> > +@smallexample
> > +#define __kernel
> > +#define __user    __attribute__ ((untrusted))
> > +void __kernel *p_kernel;
> > +void __user *p_user;
> > +
> > +/* With the above, the following assignment should be diagnosed as
> > an error.  */
> > +p_user = p_kernel;
> > +@end smallexample
> > +
> > +The NULL pointer is treated as being usable with both trusted and
> > +untrusted pointers.
> > +
> >   @item unused
> >   @cindex @code{unused} type attribute
> >   When attached to a type (including a @code{union} or a
> > @code{struct}),
> > diff --git a/gcc/print-tree.c b/gcc/print-tree.c
> > index d1fbd044c27..e5123807521 100644
> > --- a/gcc/print-tree.c
> > +++ b/gcc/print-tree.c
> > @@ -640,6 +640,9 @@ print_node (FILE *file, const char *prefix, tree
> > node, int indent,
> >         if (TYPE_RESTRICT (node))
> >         fputs (" restrict", file);
> >   
> > +      if (TYPE_UNTRUSTED (node))
> > +       fputs (" untrusted", file);
> > +
> >         if (TYPE_LANG_FLAG_0 (node))
> >         fputs (" type_0", file);
> >         if (TYPE_LANG_FLAG_1 (node))
> > diff --git a/gcc/testsuite/c-c++-common/attr-untrusted-1.c
> > b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
> > new file mode 100644
> > index 00000000000..84a217fc59f
> > --- /dev/null
> > +++ b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
> > @@ -0,0 +1,165 @@
> > +#define __kernel
> > +#define __user __attribute__((untrusted))
> > +#define __iomem
> > +#define __percpu
> > +#define __rcu
> > +
> > +void *p;
> > +void __kernel *p_kernel;
> > +void __user *p_user;
> > +void __iomem *p_iomem;
> > +void __percpu *p_percpu;
> > +void __rcu *p_rcu;
> > +
> > +#define NULL ((void *)0)
> > +
> > +extern void accepts_p (void *); /* { dg-message "24: expected 'void
> > \\*' but argument is of type '__attribute__\\(\\(untrusted\\)\\) void
> > \\*'" "" { target c } } */
> > +/* { dg-message "24:  initializing argument 1 of 'void
> > accepts_p\\(void\\*\\)'" "" { target c++ } .-1 } */
> > +extern void accepts_p_kernel (void __kernel *);
> > +extern void accepts_p_user (void __user *);
> > +
> > +void test_argpass_to_p (void)
> > +{
> > +  accepts_p (p);
> > +  accepts_p (p_kernel);
> > +  accepts_p (p_user); /* { dg-error "passing argument 1 of
> > 'accepts_p' from pointer with different trust level" "" { target c }
> > } */
> > +  /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-1 } */
> > +}
> > +
> > +void test_init_p (void)
> > +{
> > +  void *local_p_1 = p;
> > +  void *local_p_2 = p_kernel;
> > +  void *local_p_3 = p_user; /* { dg-error "initialization from
> > pointer with different trust level" "" { target c } } */
> > +  /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +  /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +}
> > +
> > +void test_init_p_kernel (void)
> > +{
> > +  void __kernel *local_p_1 = p;
> > +  void __kernel *local_p_2 = p_kernel;
> > +  void __kernel *local_p_3 = p_user; /* { dg-error "initialization
> > from pointer with different trust level" "" { target c } } */
> > +  /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +  /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +}
> > +
> > +void test_init_p_user (void)
> > +{
> > +  void __user *local_p_1 = p; /* { dg-error "initialization from
> > pointer with different trust level" "" { target c } } */
> > +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
> > \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +  void __user *local_p_2 = p_kernel; /* { dg-error "initialization
> > from pointer with different trust level" "" { target c } } */
> > +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
> > \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +  void __user *local_p_3 = p_user;
> > +  void __user *local_p_4 = NULL;
> > +}
> > +
> > +void test_assign_to_p (void)
> > +{
> > +  p = p;
> > +  p = p_kernel;
> > +  p = p_user; /* { dg-error "assignment from pointer with different
> > trust level" "" { target c } } */
> > +  /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +  /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +  // etc
> > +}
> > +
> > +void test_assign_to_p_kernel (void)
> > +{
> > +  p_kernel = p;
> > +  p_kernel = p_kernel;
> > +  p_kernel = p_user; /* { dg-error "assignment from pointer with
> > different trust level" "" { target c } } */
> > +  /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +  /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +  // etc
> > +}
> > +
> > +void test_assign_to_p_user (void)
> > +{
> > +  p_user = p;  /* { dg-error "assignment from pointer with different
> > trust level" "" { target c } } */
> > +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
> > \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +  p_user = p_kernel;  /* { dg-error "assignment from pointer with
> > different trust level" "" { target c } } */
> > +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
> > \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +  p_user = p_user;
> > +  p_user = NULL;
> > +  // etc
> > +}
> > +
> > +void *test_return_p (int i)
> > +{
> > +  switch (i)
> > +    {
> > +    default:
> > +    case 0:
> > +      return p;
> > +    case 1:
> > +      return p_kernel;
> > +    case 2:
> > +      return p_user; /* { dg-error "return from pointer with
> > different trust level" "" { target c } } */
> > +      /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +      /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +    }
> > +}
> > +
> > +void __kernel *test_return_p_kernel (int i)
> > +{
> > +  switch (i)
> > +    {
> > +    default:
> > +    case 0:
> > +      return p;
> > +    case 1:
> > +      return p_kernel;
> > +    case 2:
> > +      return p_user; /* { dg-error "return from pointer with
> > different trust level" "" { target c } } */
> > +      /* { dg-message "expected 'void \\*' but pointer is of type
> > '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
> > */
> > +      /* { dg-error "invalid conversion from
> > '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
> > target c++ } .-2 } */
> > +    }
> > +}
> > +
> > +void __user *
> > +test_return_p_user (int i)
> > +{
> > +  switch (i)
> > +    {
> > +    default:
> > +    case 0:
> > +      return p; /* { dg-error "return from pointer with different
> > trust level" "" { target c } } */
> > +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\)
> > void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +    case 1:
> > +      return p_kernel; /* { dg-error "return from pointer with
> > different trust level" "" { target c } } */
> > +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\)
> > void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
> > +    case 2:
> > +      return p_user;
> > +    case 3:
> > +      return NULL;
> > +    }
> > +}
> > +
> > +void test_cast_k_to_u (void)
> > +{
> > +  p_user = (void __user *)p_kernel;
> > +}
> > +
> > +void test_cast_u_to_k (void)
> > +{
> > +  p_kernel = (void __kernel *)p_user;
> > +}
> > +
> > +int test_deref_read (int __user *p)
> > +{
> > +  return *p; // FIXME: should this be allowed directly?
> > +}
> > +
> > +void test_deref_write (int __user *p, int i)
> > +{
> > +  *p = i; // FIXME: should this be allowed directly?
> > +}
> > +
> > +typedef struct foo { int i; } __user *foo_ptr_t;
> > +
> > +void __user *
> > +test_pass_through (void __user *ptr)
> > +{
> > +  return ptr;
> > +}
> > diff --git a/gcc/tree-core.h b/gcc/tree-core.h
> > index 8ab119dc9a2..35a7f50c06c 100644
> > --- a/gcc/tree-core.h
> > +++ b/gcc/tree-core.h
> > @@ -604,7 +604,8 @@ enum cv_qualifier {
> >     TYPE_QUAL_CONST    = 0x1,
> >     TYPE_QUAL_VOLATILE = 0x2,
> >     TYPE_QUAL_RESTRICT = 0x4,
> > -  TYPE_QUAL_ATOMIC   = 0x8
> > +  TYPE_QUAL_ATOMIC   = 0x8,
> > +  TYPE_QUAL_UNTRUSTED = 0x10
> >   };
> >   
> >   /* Standard named or nameless data types of the C compiler.  */
> > @@ -1684,7 +1685,8 @@ struct GTY(()) tree_type_common {
> >     unsigned typeless_storage : 1;
> >     unsigned empty_flag : 1;
> >     unsigned indivisible_p : 1;
> > -  unsigned spare : 16;
> > +  unsigned untrusted_flag : 1;
> > +  unsigned spare : 15;
> >   
> >     alias_set_type alias_set;
> >     tree pointer_to;
> > diff --git a/gcc/tree.c b/gcc/tree.c
> > index 845228a055b..3600639d985 100644
> > --- a/gcc/tree.c
> > +++ b/gcc/tree.c
> > @@ -5379,6 +5379,7 @@ set_type_quals (tree type, int type_quals)
> >     TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0;
> >     TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0;
> >     TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0;
> > +  TYPE_UNTRUSTED (type) = (type_quals & TYPE_QUAL_UNTRUSTED) != 0;
> >     TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals);
> >   }
> >   
> > diff --git a/gcc/tree.h b/gcc/tree.h
> > index f62c00bc870..caab575b210 100644
> > --- a/gcc/tree.h
> > +++ b/gcc/tree.h
> > @@ -2197,6 +2197,10 @@ extern tree vector_element_bits_tree
> > (const_tree);
> >      the term.  */
> >   #define TYPE_RESTRICT(NODE) (TYPE_CHECK (NODE)-
> > >type_common.restrict_flag)
> >   
> > +/* Nonzero in a type considered "untrusted" - values should be
> > treated as
> > +   under attacker control.  */
> > +#define TYPE_UNTRUSTED(NODE) (TYPE_CHECK (NODE)-
> > >type_common.untrusted_flag)
> > +
> >   /* If nonzero, type's name shouldn't be emitted into debug info. 
> > */
> >   #define TYPE_NAMELESS(NODE) (TYPE_CHECK (NODE)-
> > >base.u.bits.nameless_flag)
> >   
> > @@ -2221,6 +2225,7 @@ extern tree vector_element_bits_tree
> > (const_tree);
> >           | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
> >           | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)             \
> >           | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
> > +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)       \
> >           | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE)))))
> >   
> >   /* The same as TYPE_QUALS without the address space
> > qualifications.  */
> > @@ -2228,14 +2233,16 @@ extern tree vector_element_bits_tree
> > (const_tree);
> >     ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)            \
> >           | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
> >           | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)             \
> > -         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> > +         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
> > +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
> >   
> >   /* The same as TYPE_QUALS without the address space and atomic
> >      qualifications.  */
> >   #define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE)              \
> >     ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)            \
> >           | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
> > -         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
> > +         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
> > +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
> >   
> >   /* These flags are available for each language front end to use
> > internally.  */
> >   #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)-
> > >type_common.lang_flag_0)
> > 
>
  
Martin Sebor Jan. 6, 2022, 6:59 p.m. UTC | #3
On 1/6/22 8:10 AM, David Malcolm wrote:
> On Thu, 2021-12-09 at 15:54 -0700, Martin Sebor wrote:
>> On 11/13/21 1:37 PM, David Malcolm via Gcc-patches wrote:
>>> This patch adds a new:
>>>
>>>     __attribute__((untrusted))
>>>
>>> for use by the C front-end, intended for use by the Linux kernel for
>>> use with "__user", but which could be used by other operating system
>>> kernels, and potentialy by other projects.
>>
>> It looks like untrusted is a type attribute (rather than one
>> that applies to variables and/or function return values or
>> writeable by-reference arguments).  I find that quite surprising.
> 
> FWIW I initially tried implementing it on pointer types, but doing it
> on the underlying type was much cleaner.
> 
>>    I'm used to thinking of trusted vs tainted as dynamic properties
>> of data so I'm having trouble deciding what to think about
>> the attribute applying to types.  Can you explain why it's
>> useful on types?
> 
> A type system *is* a way of detecting problems involving dynamic
> properties of data.  Ultimately all we have at runtime is a collection
> of bits; the toolchain has the concept of types as a way to allow us to
> reason about properies of those bits without requiring a full cross-TU
> analysis (to try to figure out that e.g. x is, say, a 32 bit unsigned
> integer), and to document these properties clearly to human readers of
> the code.

I understand that relying on the type system is a way to do it.
It just doesn't seem like a very good way in a loosely typed
language like C (or C++).

> 
> I see this as working like a qualifier (rather like "const" and
> "volatile"), in that an
>    untrusted char *
> when dereferenced gives you an
>    untrusted char

Dereferencing a const char* yields a const char lvalue that
implicitly converts to an unqualified value of the referenced
object.  The qualifier is lost in the conversion, so modeling
taint/trust this way will also lose the property in the same
contexts.  It sounds to me like the concept you're modeling
might be more akin to a type specifier (maybe like _Atomic,
although that still converts to the underlying type).

> 
> The intent is to have a way of treating the values as "actively
> hostile", so that code analyzers can assume the worst possible values
> for such types (or more glibly, that we're dealing with data from Satan
> rather than from Murphy).
> 
> Such types are also relevant to infoleaks: writing sensitive
> information to an untrusted value can be detected relatively easily
> with this approach, by checking the type of the value - the types
> express the trust boundary
> 
> Doing this with qualifiers allows us to use the C type system to detect
> these kinds of issues without having to add a full cross-TU
> interprocedural analysis, and documents it to human readers of the
> code.   Compare with const-correctness; we can have an analogous
> "trust-correctness".

The problem with const-correctness in C is that it's so easily
lost (like with strchr, or in the lvalue-rvalue conversion).
This is also why I'm skeptical of the type-based approach here.

> 
>>
>> I'd expect the taint property of a type to be quickly lost as
>> an object of the type is passed through existing APIs (e.g.,
>> a char array manipulated by string functions like strchr).
> 
> FWIW you can't directly pass an attacker-controlled buffer to strchr:
> strchr requires there to be a 0-terminator to the array; if the array's
> content is untrusted then the attacker might not have 0-terminated it.

strchr is just an example of the many functions that in my mind
make the type-based approach less than ideal.  If the untrusted
string was known to be nul-teminated, strchr still couldn't be
used without losing the property.  Ditto for memchr.  It seems
that all sanitization would either have to be written from
scratch, without relying on existing utility functions, or by
providing wrappers that called the common utility functions
after removing the qualifier from the tainted data even before
the santization was complete.  That would obviously be error-
prone, but it's something that would be made much more robust
by tracking the taint independently of the data type.

Martin

> 
> As implemented, the patch doesn't complain about this, though maybe it
> should.
> 
> The main point here is to support the existing __user annotation used
> by the Linux kernel, in particular, copy_from_user and copy_to_user.
> 
>>
>> (I usually look at tests to help me understand the design of
>> a change but I couldn't find an answer to my question in those
>> in the patch.)
> 
> The patch kit was rather unclear on this, due to the use of two
> different approaches (custom address spaces vs this untrusted
> attribute).  Sorry about this.
> 
> Patches 4a and 4b in the kit add test-uaccess.h (to
> gcc/testsuite/gcc.dg/analyzer) which supplies "__user"; see the tests
> that use "test-uaccess.h" in patch 3:
>   [PATCH 3/6] analyzer: implement infoleak detection
>      https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584377.html
> and in patch 5:
>   [PATCH 5/6] analyzer: use region::untrusted_p in taint detection
>     https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584374.html
> 
> (sorry about messing up the order of the patches).
> 
> Patch 4a here:
>   [PATCH 4a/6] analyzer: implement region::untrusted_p in terms of custom address spaces
>     https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584371.html
> implements "__user" as a custom address space,
> 
> whereas patch 4b here:
> 
>   [PATCH 4b/6] analyzer: implement region::untrusted_p in terms of __attribute__((untrusted))
>      https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584373.html
> 
> implements "__user" to be __attribute__((untrusted)).
> 
> Perhaps I should drop the custom address space versions of the patches
> and post a version of the kit that simply uses the attribute?
> 
> Dave
> 
> 
>>
>> Thanks
>> Martin
>>
>> PS I found one paper online that discusses type-based taint
>> analysis in Java but not much more.  I only quickly skimmed
>> the paper and although it conceptually makes sense I'm still
>> having difficulties seeing how it would be useful in C.
>>
>>>
>>> Known issues:
>>> - at least one TODO in handle_untrusted_attribute
>>> - should it be permitted to dereference an untrusted pointer?  The
>>> patch
>>>     currently allows this
>>>
>>> gcc/c-family/ChangeLog:
>>>          * c-attribs.c (c_common_attribute_table): Add "untrusted".
>>>          (build_untrusted_type): New.
>>>          (handle_untrusted_attribute): New.
>>>          * c-pretty-print.c (pp_c_cv_qualifiers): Handle
>>>          TYPE_QUAL_UNTRUSTED.
>>>
>>> gcc/c/ChangeLog:
>>>          * c-typeck.c (convert_for_assignment): Complain if the trust
>>>          levels vary when assigning a non-NULL pointer.
>>>
>>> gcc/ChangeLog:
>>>          * doc/extend.texi (Common Type Attributes): Add "untrusted".
>>>          * print-tree.c (print_node): Handle TYPE_UNTRUSTED.
>>>          * tree-core.h (enum cv_qualifier): Add TYPE_QUAL_UNTRUSTED.
>>>          (struct tree_type_common): Assign one of the spare bits to a
>>> new
>>>          "untrusted_flag".
>>>          * tree.c (set_type_quals): Handle TYPE_QUAL_UNTRUSTED.
>>>          * tree.h (TYPE_QUALS): Likewise.
>>>          (TYPE_QUALS_NO_ADDR_SPACE): Likewise.
>>>          (TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC): Likewise.
>>>
>>> gcc/testsuite/ChangeLog:
>>>          * c-c++-common/attr-untrusted-1.c: New test.
>>>
>>> Signed-off-by: David Malcolm <dmalcolm@redhat.com>
>>> ---
>>>    gcc/c-family/c-attribs.c                      |  59 +++++++
>>>    gcc/c-family/c-pretty-print.c                 |   2 +
>>>    gcc/c/c-typeck.c                              |  64 +++++++
>>>    gcc/doc/extend.texi                           |  25 +++
>>>    gcc/print-tree.c                              |   3 +
>>>    gcc/testsuite/c-c++-common/attr-untrusted-1.c | 165
>>> ++++++++++++++++++
>>>    gcc/tree-core.h                               |   6 +-
>>>    gcc/tree.c                                    |   1 +
>>>    gcc/tree.h                                    |  11 +-
>>>    9 files changed, 332 insertions(+), 4 deletions(-)
>>>    create mode 100644 gcc/testsuite/c-c++-common/attr-untrusted-1.c
>>>
>>> diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
>>> index 007b928c54b..100c2dabab2 100644
>>> --- a/gcc/c-family/c-attribs.c
>>> +++ b/gcc/c-family/c-attribs.c
>>> @@ -136,6 +136,7 @@ static tree handle_warn_unused_result_attribute
>>> (tree *, tree, tree, int,
>>>                                                   bool *);
>>>    static tree handle_access_attribute (tree *, tree, tree, int, bool
>>> *);
>>>    
>>> +static tree handle_untrusted_attribute (tree *, tree, tree, int,
>>> bool *);
>>>    static tree handle_sentinel_attribute (tree *, tree, tree, int,
>>> bool *);
>>>    static tree handle_type_generic_attribute (tree *, tree, tree, int,
>>> bool *);
>>>    static tree handle_alloc_size_attribute (tree *, tree, tree, int,
>>> bool *);
>>> @@ -536,6 +537,8 @@ const struct attribute_spec
>>> c_common_attribute_table[] =
>>>                                handle_special_var_sec_attribute,
>>> attr_section_exclusions },
>>>      { "access",               1, 3, false, true, true, false,
>>>                                handle_access_attribute, NULL },
>>> +  { "untrusted",             0, 0, false,  true, false, true,
>>> +                             handle_untrusted_attribute, NULL },
>>>      /* Attributes used by Objective-C.  */
>>>      { "NSObject",                     0, 0, true, false, false,
>>> false,
>>>                                handle_nsobject_attribute, NULL },
>>> @@ -5224,6 +5227,62 @@ build_attr_access_from_parms (tree parms, bool
>>> skip_voidptr)
>>>      return build_tree_list (name, attrargs);
>>>    }
>>>    
>>> +/* Build (or reuse) a type based on BASE_TYPE, but with
>>> +   TYPE_QUAL_UNTRUSTED.  */
>>> +
>>> +static tree
>>> +build_untrusted_type (tree base_type)
>>> +{
>>> +  int base_type_quals = TYPE_QUALS (base_type);
>>> +  return build_qualified_type (base_type,
>>> +                              base_type_quals |
>>> TYPE_QUAL_UNTRUSTED);
>>> +}
>>> +
>>> +/* Handle an "untrusted" attribute; arguments as in
>>> +   struct attribute_spec.handler.  */
>>> +
>>> +static tree
>>> +handle_untrusted_attribute (tree *node, tree ARG_UNUSED (name),
>>> +                           tree ARG_UNUSED (args), int ARG_UNUSED
>>> (flags),
>>> +                           bool *no_add_attrs)
>>> +{
>>> +  if (TREE_CODE (*node) == POINTER_TYPE)
>>> +    {
>>> +      tree base_type = TREE_TYPE (*node);
>>> +      tree untrusted_base_type = build_untrusted_type (base_type);
>>> +      *node = build_pointer_type (untrusted_base_type);
>>> +      *no_add_attrs = true; /* OK */
>>> +      return NULL_TREE;
>>> +    }
>>> +  else if (TREE_CODE (*node) == FUNCTION_TYPE)
>>> +    {
>>> +      tree return_type = TREE_TYPE (*node);
>>> +      if (TREE_CODE (return_type) == POINTER_TYPE)
>>> +       {
>>> +         tree base_type = TREE_TYPE (return_type);
>>> +         tree untrusted_base_type = build_untrusted_type
>>> (base_type);
>>> +         tree untrusted_return_type = build_pointer_type
>>> (untrusted_base_type);
>>> +         tree fn_type = build_function_type (untrusted_return_type,
>>> +                                             TYPE_ARG_TYPES
>>> (*node));
>>> +         *node = fn_type;
>>> +         *no_add_attrs = true; /* OK */
>>> +         return NULL_TREE;
>>> +       }
>>> +      else
>>> +       {
>>> +         gcc_unreachable (); // TODO
>>> +       }
>>> +    }
>>> +  else
>>> +    {
>>> +      tree base_type = *node;
>>> +      tree untrusted_base_type = build_untrusted_type (base_type);
>>> +      *node = untrusted_base_type;
>>> +      *no_add_attrs = true; /* OK */
>>> +      return NULL_TREE;
>>> +    }
>>> +}
>>> +
>>>    /* Handle a "nothrow" attribute; arguments as in
>>>       struct attribute_spec.handler.  */
>>>    
>>> diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-
>>> print.c
>>> index a987da46d6d..120e1e6d167 100644
>>> --- a/gcc/c-family/c-pretty-print.c
>>> +++ b/gcc/c-family/c-pretty-print.c
>>> @@ -191,6 +191,8 @@ pp_c_cv_qualifiers (c_pretty_printer *pp, int
>>> qualifiers, bool func_type)
>>>      if (qualifiers & TYPE_QUAL_RESTRICT)
>>>        pp_c_ws_string (pp, (flag_isoc99 && !c_dialect_cxx ()
>>>                           ? "restrict" : "__restrict__"));
>>> +  if (qualifiers & TYPE_QUAL_UNTRUSTED)
>>> +    pp_c_ws_string (pp, "__attribute__((untrusted))");
>>>    }
>>>    
>>>    /* Pretty-print T using the type-cast notation '( type-name )'.  */
>>> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
>>> index 782414f8c8c..44de82b99ba 100644
>>> --- a/gcc/c/c-typeck.c
>>> +++ b/gcc/c/c-typeck.c
>>> @@ -7284,6 +7284,70 @@ convert_for_assignment (location_t location,
>>> location_t expr_loc, tree type,
>>>            return error_mark_node;
>>>          }
>>>    
>>> +      /* Untrusted vs trusted pointers, but allowing NULL to be used
>>> +        for everything.  */
>>> +      if (TYPE_UNTRUSTED (ttl) != TYPE_UNTRUSTED (ttr)
>>> +         && !null_pointer_constant_p (rhs))
>>> +       {
>>> +         auto_diagnostic_group d;
>>> +         bool diagnosed = true;
>>> +         switch (errtype)
>>> +           {
>>> +           case ic_argpass:
>>> +             {
>>> +               const char msg[] = G_("passing argument %d of %qE
>>> from "
>>> +                                     "pointer with different trust
>>> level");
>>> +               if (warnopt)
>>> +                 diagnosed
>>> +                   = warning_at (expr_loc, warnopt, msg, parmnum,
>>> rname);
>>> +               else
>>> +                 error_at (expr_loc, msg, parmnum, rname);
>>> +             break;
>>> +             }
>>> +           case ic_assign:
>>> +             {
>>> +               const char msg[] = G_("assignment from pointer with "
>>> +                                     "different trust level");
>>> +               if (warnopt)
>>> +                 warning_at (location, warnopt, msg);
>>> +               else
>>> +                 error_at (location, msg);
>>> +               break;
>>> +             }
>>> +           case ic_init:
>>> +             {
>>> +               const char msg[] = G_("initialization from pointer
>>> with "
>>> +                                     "different trust level");
>>> +               if (warnopt)
>>> +                 warning_at (location, warnopt, msg);
>>> +               else
>>> +                 error_at (location, msg);
>>> +               break;
>>> +             }
>>> +           case ic_return:
>>> +             {
>>> +               const char msg[] = G_("return from pointer with "
>>> +                                     "different trust level");
>>> +               if (warnopt)
>>> +                 warning_at (location, warnopt, msg);
>>> +               else
>>> +                 error_at (location, msg);
>>> +               break;
>>> +             }
>>> +           default:
>>> +             gcc_unreachable ();
>>> +           }
>>> +         if (diagnosed)
>>> +           {
>>> +             if (errtype == ic_argpass)
>>> +               inform_for_arg (fundecl, expr_loc, parmnum, type,
>>> rhstype);
>>> +             else
>>> +               inform (location, "expected %qT but pointer is of
>>> type %qT",
>>> +                       type, rhstype);
>>> +           }
>>> +         return error_mark_node;
>>> +       }
>>> +
>>>          /* Check if the right-hand side has a format attribute but
>>> the
>>>           left-hand side doesn't.  */
>>>          if (warn_suggest_attribute_format
>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>> index 6e6c580e329..e9f47519df2 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -8770,6 +8770,31 @@ pid_t wait (wait_status_ptr_t p)
>>>    @}
>>>    @end smallexample
>>>    
>>> +@item untrusted
>>> +@cindex @code{untrusted} type attribute
>>> +Types marked with this attribute are treated as being ``untrusted''
>>> -
>>> +values should be treated as under attacker control.
>>> +
>>> +The C front end will issue an error diagnostic on attempts to assign
>>> +pointer values between untrusted and trusted pointer types without
>>> +an explicit cast.
>>> +
>>> +For example, when implementing an operating system kernel, one
>>> +might write
>>> +
>>> +@smallexample
>>> +#define __kernel
>>> +#define __user    __attribute__ ((untrusted))
>>> +void __kernel *p_kernel;
>>> +void __user *p_user;
>>> +
>>> +/* With the above, the following assignment should be diagnosed as
>>> an error.  */
>>> +p_user = p_kernel;
>>> +@end smallexample
>>> +
>>> +The NULL pointer is treated as being usable with both trusted and
>>> +untrusted pointers.
>>> +
>>>    @item unused
>>>    @cindex @code{unused} type attribute
>>>    When attached to a type (including a @code{union} or a
>>> @code{struct}),
>>> diff --git a/gcc/print-tree.c b/gcc/print-tree.c
>>> index d1fbd044c27..e5123807521 100644
>>> --- a/gcc/print-tree.c
>>> +++ b/gcc/print-tree.c
>>> @@ -640,6 +640,9 @@ print_node (FILE *file, const char *prefix, tree
>>> node, int indent,
>>>          if (TYPE_RESTRICT (node))
>>>          fputs (" restrict", file);
>>>    
>>> +      if (TYPE_UNTRUSTED (node))
>>> +       fputs (" untrusted", file);
>>> +
>>>          if (TYPE_LANG_FLAG_0 (node))
>>>          fputs (" type_0", file);
>>>          if (TYPE_LANG_FLAG_1 (node))
>>> diff --git a/gcc/testsuite/c-c++-common/attr-untrusted-1.c
>>> b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
>>> new file mode 100644
>>> index 00000000000..84a217fc59f
>>> --- /dev/null
>>> +++ b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
>>> @@ -0,0 +1,165 @@
>>> +#define __kernel
>>> +#define __user __attribute__((untrusted))
>>> +#define __iomem
>>> +#define __percpu
>>> +#define __rcu
>>> +
>>> +void *p;
>>> +void __kernel *p_kernel;
>>> +void __user *p_user;
>>> +void __iomem *p_iomem;
>>> +void __percpu *p_percpu;
>>> +void __rcu *p_rcu;
>>> +
>>> +#define NULL ((void *)0)
>>> +
>>> +extern void accepts_p (void *); /* { dg-message "24: expected 'void
>>> \\*' but argument is of type '__attribute__\\(\\(untrusted\\)\\) void
>>> \\*'" "" { target c } } */
>>> +/* { dg-message "24:  initializing argument 1 of 'void
>>> accepts_p\\(void\\*\\)'" "" { target c++ } .-1 } */
>>> +extern void accepts_p_kernel (void __kernel *);
>>> +extern void accepts_p_user (void __user *);
>>> +
>>> +void test_argpass_to_p (void)
>>> +{
>>> +  accepts_p (p);
>>> +  accepts_p (p_kernel);
>>> +  accepts_p (p_user); /* { dg-error "passing argument 1 of
>>> 'accepts_p' from pointer with different trust level" "" { target c }
>>> } */
>>> +  /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-1 } */
>>> +}
>>> +
>>> +void test_init_p (void)
>>> +{
>>> +  void *local_p_1 = p;
>>> +  void *local_p_2 = p_kernel;
>>> +  void *local_p_3 = p_user; /* { dg-error "initialization from
>>> pointer with different trust level" "" { target c } } */
>>> +  /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +  /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +}
>>> +
>>> +void test_init_p_kernel (void)
>>> +{
>>> +  void __kernel *local_p_1 = p;
>>> +  void __kernel *local_p_2 = p_kernel;
>>> +  void __kernel *local_p_3 = p_user; /* { dg-error "initialization
>>> from pointer with different trust level" "" { target c } } */
>>> +  /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +  /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +}
>>> +
>>> +void test_init_p_user (void)
>>> +{
>>> +  void __user *local_p_1 = p; /* { dg-error "initialization from
>>> pointer with different trust level" "" { target c } } */
>>> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
>>> \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +  void __user *local_p_2 = p_kernel; /* { dg-error "initialization
>>> from pointer with different trust level" "" { target c } } */
>>> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
>>> \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +  void __user *local_p_3 = p_user;
>>> +  void __user *local_p_4 = NULL;
>>> +}
>>> +
>>> +void test_assign_to_p (void)
>>> +{
>>> +  p = p;
>>> +  p = p_kernel;
>>> +  p = p_user; /* { dg-error "assignment from pointer with different
>>> trust level" "" { target c } } */
>>> +  /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +  /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +  // etc
>>> +}
>>> +
>>> +void test_assign_to_p_kernel (void)
>>> +{
>>> +  p_kernel = p;
>>> +  p_kernel = p_kernel;
>>> +  p_kernel = p_user; /* { dg-error "assignment from pointer with
>>> different trust level" "" { target c } } */
>>> +  /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +  /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +  // etc
>>> +}
>>> +
>>> +void test_assign_to_p_user (void)
>>> +{
>>> +  p_user = p;  /* { dg-error "assignment from pointer with different
>>> trust level" "" { target c } } */
>>> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
>>> \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +  p_user = p_kernel;  /* { dg-error "assignment from pointer with
>>> different trust level" "" { target c } } */
>>> +  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void
>>> \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +  p_user = p_user;
>>> +  p_user = NULL;
>>> +  // etc
>>> +}
>>> +
>>> +void *test_return_p (int i)
>>> +{
>>> +  switch (i)
>>> +    {
>>> +    default:
>>> +    case 0:
>>> +      return p;
>>> +    case 1:
>>> +      return p_kernel;
>>> +    case 2:
>>> +      return p_user; /* { dg-error "return from pointer with
>>> different trust level" "" { target c } } */
>>> +      /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +      /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +    }
>>> +}
>>> +
>>> +void __kernel *test_return_p_kernel (int i)
>>> +{
>>> +  switch (i)
>>> +    {
>>> +    default:
>>> +    case 0:
>>> +      return p;
>>> +    case 1:
>>> +      return p_kernel;
>>> +    case 2:
>>> +      return p_user; /* { dg-error "return from pointer with
>>> different trust level" "" { target c } } */
>>> +      /* { dg-message "expected 'void \\*' but pointer is of type
>>> '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 }
>>> */
>>> +      /* { dg-error "invalid conversion from
>>> '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" {
>>> target c++ } .-2 } */
>>> +    }
>>> +}
>>> +
>>> +void __user *
>>> +test_return_p_user (int i)
>>> +{
>>> +  switch (i)
>>> +    {
>>> +    default:
>>> +    case 0:
>>> +      return p; /* { dg-error "return from pointer with different
>>> trust level" "" { target c } } */
>>> +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\)
>>> void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +    case 1:
>>> +      return p_kernel; /* { dg-error "return from pointer with
>>> different trust level" "" { target c } } */
>>> +      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\)
>>> void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
>>> +    case 2:
>>> +      return p_user;
>>> +    case 3:
>>> +      return NULL;
>>> +    }
>>> +}
>>> +
>>> +void test_cast_k_to_u (void)
>>> +{
>>> +  p_user = (void __user *)p_kernel;
>>> +}
>>> +
>>> +void test_cast_u_to_k (void)
>>> +{
>>> +  p_kernel = (void __kernel *)p_user;
>>> +}
>>> +
>>> +int test_deref_read (int __user *p)
>>> +{
>>> +  return *p; // FIXME: should this be allowed directly?
>>> +}
>>> +
>>> +void test_deref_write (int __user *p, int i)
>>> +{
>>> +  *p = i; // FIXME: should this be allowed directly?
>>> +}
>>> +
>>> +typedef struct foo { int i; } __user *foo_ptr_t;
>>> +
>>> +void __user *
>>> +test_pass_through (void __user *ptr)
>>> +{
>>> +  return ptr;
>>> +}
>>> diff --git a/gcc/tree-core.h b/gcc/tree-core.h
>>> index 8ab119dc9a2..35a7f50c06c 100644
>>> --- a/gcc/tree-core.h
>>> +++ b/gcc/tree-core.h
>>> @@ -604,7 +604,8 @@ enum cv_qualifier {
>>>      TYPE_QUAL_CONST    = 0x1,
>>>      TYPE_QUAL_VOLATILE = 0x2,
>>>      TYPE_QUAL_RESTRICT = 0x4,
>>> -  TYPE_QUAL_ATOMIC   = 0x8
>>> +  TYPE_QUAL_ATOMIC   = 0x8,
>>> +  TYPE_QUAL_UNTRUSTED = 0x10
>>>    };
>>>    
>>>    /* Standard named or nameless data types of the C compiler.  */
>>> @@ -1684,7 +1685,8 @@ struct GTY(()) tree_type_common {
>>>      unsigned typeless_storage : 1;
>>>      unsigned empty_flag : 1;
>>>      unsigned indivisible_p : 1;
>>> -  unsigned spare : 16;
>>> +  unsigned untrusted_flag : 1;
>>> +  unsigned spare : 15;
>>>    
>>>      alias_set_type alias_set;
>>>      tree pointer_to;
>>> diff --git a/gcc/tree.c b/gcc/tree.c
>>> index 845228a055b..3600639d985 100644
>>> --- a/gcc/tree.c
>>> +++ b/gcc/tree.c
>>> @@ -5379,6 +5379,7 @@ set_type_quals (tree type, int type_quals)
>>>      TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0;
>>>      TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0;
>>>      TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0;
>>> +  TYPE_UNTRUSTED (type) = (type_quals & TYPE_QUAL_UNTRUSTED) != 0;
>>>      TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals);
>>>    }
>>>    
>>> diff --git a/gcc/tree.h b/gcc/tree.h
>>> index f62c00bc870..caab575b210 100644
>>> --- a/gcc/tree.h
>>> +++ b/gcc/tree.h
>>> @@ -2197,6 +2197,10 @@ extern tree vector_element_bits_tree
>>> (const_tree);
>>>       the term.  */
>>>    #define TYPE_RESTRICT(NODE) (TYPE_CHECK (NODE)-
>>>> type_common.restrict_flag)
>>>    
>>> +/* Nonzero in a type considered "untrusted" - values should be
>>> treated as
>>> +   under attacker control.  */
>>> +#define TYPE_UNTRUSTED(NODE) (TYPE_CHECK (NODE)-
>>>> type_common.untrusted_flag)
>>> +
>>>    /* If nonzero, type's name shouldn't be emitted into debug info.
>>> */
>>>    #define TYPE_NAMELESS(NODE) (TYPE_CHECK (NODE)-
>>>> base.u.bits.nameless_flag)
>>>    
>>> @@ -2221,6 +2225,7 @@ extern tree vector_element_bits_tree
>>> (const_tree);
>>>            | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
>>>            | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)             \
>>>            | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
>>> +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)       \
>>>            | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE)))))
>>>    
>>>    /* The same as TYPE_QUALS without the address space
>>> qualifications.  */
>>> @@ -2228,14 +2233,16 @@ extern tree vector_element_bits_tree
>>> (const_tree);
>>>      ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)            \
>>>            | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
>>>            | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)             \
>>> -         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
>>> +         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
>>> +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
>>>    
>>>    /* The same as TYPE_QUALS without the address space and atomic
>>>       qualifications.  */
>>>    #define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE)              \
>>>      ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)            \
>>>            | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)         \
>>> -         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
>>> +         | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)         \
>>> +         | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
>>>    
>>>    /* These flags are available for each language front end to use
>>> internally.  */
>>>    #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)-
>>>> type_common.lang_flag_0)
>>>
>>
> 
>
  

Patch

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 007b928c54b..100c2dabab2 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -136,6 +136,7 @@  static tree handle_warn_unused_result_attribute (tree *, tree, tree, int,
 						 bool *);
 static tree handle_access_attribute (tree *, tree, tree, int, bool *);
 
+static tree handle_untrusted_attribute (tree *, tree, tree, int, bool *);
 static tree handle_sentinel_attribute (tree *, tree, tree, int, bool *);
 static tree handle_type_generic_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
@@ -536,6 +537,8 @@  const struct attribute_spec c_common_attribute_table[] =
 			      handle_special_var_sec_attribute, attr_section_exclusions },
   { "access",		      1, 3, false, true, true, false,
 			      handle_access_attribute, NULL },
+  { "untrusted",	      0, 0, false,  true, false, true,
+			      handle_untrusted_attribute, NULL },
   /* Attributes used by Objective-C.  */
   { "NSObject",		      0, 0, true, false, false, false,
 			      handle_nsobject_attribute, NULL },
@@ -5224,6 +5227,62 @@  build_attr_access_from_parms (tree parms, bool skip_voidptr)
   return build_tree_list (name, attrargs);
 }
 
+/* Build (or reuse) a type based on BASE_TYPE, but with
+   TYPE_QUAL_UNTRUSTED.  */
+
+static tree
+build_untrusted_type (tree base_type)
+{
+  int base_type_quals = TYPE_QUALS (base_type);
+  return build_qualified_type (base_type,
+			       base_type_quals | TYPE_QUAL_UNTRUSTED);
+}
+
+/* Handle an "untrusted" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_untrusted_attribute (tree *node, tree ARG_UNUSED (name),
+			    tree ARG_UNUSED (args), int ARG_UNUSED (flags),
+			    bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) == POINTER_TYPE)
+    {
+      tree base_type = TREE_TYPE (*node);
+      tree untrusted_base_type = build_untrusted_type (base_type);
+      *node = build_pointer_type (untrusted_base_type);
+      *no_add_attrs = true; /* OK */
+      return NULL_TREE;
+    }
+  else if (TREE_CODE (*node) == FUNCTION_TYPE)
+    {
+      tree return_type = TREE_TYPE (*node);
+      if (TREE_CODE (return_type) == POINTER_TYPE)
+	{
+	  tree base_type = TREE_TYPE (return_type);
+	  tree untrusted_base_type = build_untrusted_type (base_type);
+	  tree untrusted_return_type = build_pointer_type (untrusted_base_type);
+	  tree fn_type = build_function_type (untrusted_return_type,
+					      TYPE_ARG_TYPES (*node));
+	  *node = fn_type;
+	  *no_add_attrs = true; /* OK */
+	  return NULL_TREE;
+	}
+      else
+	{
+	  gcc_unreachable (); // TODO
+	}
+    }
+  else
+    {
+      tree base_type = *node;
+      tree untrusted_base_type = build_untrusted_type (base_type);
+      *node = untrusted_base_type;
+      *no_add_attrs = true; /* OK */
+      return NULL_TREE;
+    }
+}
+
 /* Handle a "nothrow" attribute; arguments as in
    struct attribute_spec.handler.  */
 
diff --git a/gcc/c-family/c-pretty-print.c b/gcc/c-family/c-pretty-print.c
index a987da46d6d..120e1e6d167 100644
--- a/gcc/c-family/c-pretty-print.c
+++ b/gcc/c-family/c-pretty-print.c
@@ -191,6 +191,8 @@  pp_c_cv_qualifiers (c_pretty_printer *pp, int qualifiers, bool func_type)
   if (qualifiers & TYPE_QUAL_RESTRICT)
     pp_c_ws_string (pp, (flag_isoc99 && !c_dialect_cxx ()
 			 ? "restrict" : "__restrict__"));
+  if (qualifiers & TYPE_QUAL_UNTRUSTED)
+    pp_c_ws_string (pp, "__attribute__((untrusted))");
 }
 
 /* Pretty-print T using the type-cast notation '( type-name )'.  */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 782414f8c8c..44de82b99ba 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -7284,6 +7284,70 @@  convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	  return error_mark_node;
 	}
 
+      /* Untrusted vs trusted pointers, but allowing NULL to be used
+	 for everything.  */
+      if (TYPE_UNTRUSTED (ttl) != TYPE_UNTRUSTED (ttr)
+	  && !null_pointer_constant_p (rhs))
+	{
+	  auto_diagnostic_group d;
+	  bool diagnosed = true;
+	  switch (errtype)
+	    {
+	    case ic_argpass:
+	      {
+		const char msg[] = G_("passing argument %d of %qE from "
+				      "pointer with different trust level");
+		if (warnopt)
+		  diagnosed
+		    = warning_at (expr_loc, warnopt, msg, parmnum, rname);
+		else
+		  error_at (expr_loc, msg, parmnum, rname);
+	      break;
+	      }
+	    case ic_assign:
+	      {
+		const char msg[] = G_("assignment from pointer with "
+				      "different trust level");
+		if (warnopt)
+		  warning_at (location, warnopt, msg);
+		else
+		  error_at (location, msg);
+		break;
+	      }
+	    case ic_init:
+	      {
+		const char msg[] = G_("initialization from pointer with "
+				      "different trust level");
+		if (warnopt)
+		  warning_at (location, warnopt, msg);
+		else
+		  error_at (location, msg);
+		break;
+	      }
+	    case ic_return:
+	      {
+		const char msg[] = G_("return from pointer with "
+				      "different trust level");
+		if (warnopt)
+		  warning_at (location, warnopt, msg);
+		else
+		  error_at (location, msg);
+		break;
+	      }
+	    default:
+	      gcc_unreachable ();
+	    }
+	  if (diagnosed)
+	    {
+	      if (errtype == ic_argpass)
+		inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
+	      else
+		inform (location, "expected %qT but pointer is of type %qT",
+			type, rhstype);
+	    }
+	  return error_mark_node;
+	}
+
       /* Check if the right-hand side has a format attribute but the
 	 left-hand side doesn't.  */
       if (warn_suggest_attribute_format
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 6e6c580e329..e9f47519df2 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -8770,6 +8770,31 @@  pid_t wait (wait_status_ptr_t p)
 @}
 @end smallexample
 
+@item untrusted
+@cindex @code{untrusted} type attribute
+Types marked with this attribute are treated as being ``untrusted'' -
+values should be treated as under attacker control.
+
+The C front end will issue an error diagnostic on attempts to assign
+pointer values between untrusted and trusted pointer types without
+an explicit cast.
+
+For example, when implementing an operating system kernel, one
+might write
+
+@smallexample
+#define __kernel
+#define __user    __attribute__ ((untrusted))
+void __kernel *p_kernel;
+void __user *p_user;
+
+/* With the above, the following assignment should be diagnosed as an error.  */
+p_user = p_kernel;
+@end smallexample
+
+The NULL pointer is treated as being usable with both trusted and
+untrusted pointers.
+
 @item unused
 @cindex @code{unused} type attribute
 When attached to a type (including a @code{union} or a @code{struct}),
diff --git a/gcc/print-tree.c b/gcc/print-tree.c
index d1fbd044c27..e5123807521 100644
--- a/gcc/print-tree.c
+++ b/gcc/print-tree.c
@@ -640,6 +640,9 @@  print_node (FILE *file, const char *prefix, tree node, int indent,
       if (TYPE_RESTRICT (node))
 	fputs (" restrict", file);
 
+      if (TYPE_UNTRUSTED (node))
+	fputs (" untrusted", file);
+
       if (TYPE_LANG_FLAG_0 (node))
 	fputs (" type_0", file);
       if (TYPE_LANG_FLAG_1 (node))
diff --git a/gcc/testsuite/c-c++-common/attr-untrusted-1.c b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
new file mode 100644
index 00000000000..84a217fc59f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/attr-untrusted-1.c
@@ -0,0 +1,165 @@ 
+#define __kernel
+#define __user __attribute__((untrusted))
+#define __iomem
+#define __percpu
+#define __rcu
+
+void *p;
+void __kernel *p_kernel;
+void __user *p_user;
+void __iomem *p_iomem;
+void __percpu *p_percpu;
+void __rcu *p_rcu;
+
+#define NULL ((void *)0)
+
+extern void accepts_p (void *); /* { dg-message "24: expected 'void \\*' but argument is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } } */
+/* { dg-message "24:  initializing argument 1 of 'void accepts_p\\(void\\*\\)'" "" { target c++ } .-1 } */
+extern void accepts_p_kernel (void __kernel *);
+extern void accepts_p_user (void __user *);
+
+void test_argpass_to_p (void)
+{
+  accepts_p (p);
+  accepts_p (p_kernel);
+  accepts_p (p_user); /* { dg-error "passing argument 1 of 'accepts_p' from pointer with different trust level" "" { target c } } */
+  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-1 } */
+}
+
+void test_init_p (void)
+{
+  void *local_p_1 = p;
+  void *local_p_2 = p_kernel;
+  void *local_p_3 = p_user; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+}
+
+void test_init_p_kernel (void)
+{
+  void __kernel *local_p_1 = p;
+  void __kernel *local_p_2 = p_kernel;
+  void __kernel *local_p_3 = p_user; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+}
+
+void test_init_p_user (void)
+{
+  void __user *local_p_1 = p; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+  void __user *local_p_2 = p_kernel; /* { dg-error "initialization from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+  void __user *local_p_3 = p_user;
+  void __user *local_p_4 = NULL;
+}
+
+void test_assign_to_p (void)
+{
+  p = p;
+  p = p_kernel;
+  p = p_user; /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+  // etc
+}
+
+void test_assign_to_p_kernel (void)
+{
+  p_kernel = p;
+  p_kernel = p_kernel;
+  p_kernel = p_user; /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+  /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+  // etc
+}
+
+void test_assign_to_p_user (void)
+{
+  p_user = p;  /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+  p_user = p_kernel;  /* { dg-error "assignment from pointer with different trust level" "" { target c } } */
+  /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+  p_user = p_user;
+  p_user = NULL;
+  // etc
+}
+
+void *test_return_p (int i)
+{
+  switch (i)
+    {
+    default:
+    case 0:
+      return p;
+    case 1:
+      return p_kernel;
+    case 2:
+      return p_user; /* { dg-error "return from pointer with different trust level" "" { target c } } */
+      /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+      /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+    }
+}
+
+void __kernel *test_return_p_kernel (int i)
+{
+  switch (i)
+    {
+    default:
+    case 0:
+      return p;
+    case 1:
+      return p_kernel;
+    case 2:
+      return p_user; /* { dg-error "return from pointer with different trust level" "" { target c } } */
+      /* { dg-message "expected 'void \\*' but pointer is of type '__attribute__\\(\\(untrusted\\)\\) void \\*'" "" { target c } .-1 } */
+      /* { dg-error "invalid conversion from '__attribute__\\(\\(untrusted\\)\\) void\\*' to 'void\\*'" "" { target c++ } .-2 } */
+    }
+}
+
+void __user *
+test_return_p_user (int i)
+{
+  switch (i)
+    {
+    default:
+    case 0:
+      return p; /* { dg-error "return from pointer with different trust level" "" { target c } } */
+      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+    case 1:
+      return p_kernel; /* { dg-error "return from pointer with different trust level" "" { target c } } */
+      /* { dg-message "expected '__attribute__\\(\\(untrusted\\)\\) void \\*' but pointer is of type 'void \\*'" "" { target c } .-1 } */
+    case 2:
+      return p_user;
+    case 3:
+      return NULL;
+    }
+}
+
+void test_cast_k_to_u (void)
+{
+  p_user = (void __user *)p_kernel;
+}
+
+void test_cast_u_to_k (void)
+{
+  p_kernel = (void __kernel *)p_user;
+}
+
+int test_deref_read (int __user *p)
+{
+  return *p; // FIXME: should this be allowed directly?
+}
+
+void test_deref_write (int __user *p, int i)
+{
+  *p = i; // FIXME: should this be allowed directly?
+}
+
+typedef struct foo { int i; } __user *foo_ptr_t;
+
+void __user *
+test_pass_through (void __user *ptr)
+{
+  return ptr;
+}
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 8ab119dc9a2..35a7f50c06c 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -604,7 +604,8 @@  enum cv_qualifier {
   TYPE_QUAL_CONST    = 0x1,
   TYPE_QUAL_VOLATILE = 0x2,
   TYPE_QUAL_RESTRICT = 0x4,
-  TYPE_QUAL_ATOMIC   = 0x8
+  TYPE_QUAL_ATOMIC   = 0x8,
+  TYPE_QUAL_UNTRUSTED = 0x10
 };
 
 /* Standard named or nameless data types of the C compiler.  */
@@ -1684,7 +1685,8 @@  struct GTY(()) tree_type_common {
   unsigned typeless_storage : 1;
   unsigned empty_flag : 1;
   unsigned indivisible_p : 1;
-  unsigned spare : 16;
+  unsigned untrusted_flag : 1;
+  unsigned spare : 15;
 
   alias_set_type alias_set;
   tree pointer_to;
diff --git a/gcc/tree.c b/gcc/tree.c
index 845228a055b..3600639d985 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5379,6 +5379,7 @@  set_type_quals (tree type, int type_quals)
   TYPE_VOLATILE (type) = (type_quals & TYPE_QUAL_VOLATILE) != 0;
   TYPE_RESTRICT (type) = (type_quals & TYPE_QUAL_RESTRICT) != 0;
   TYPE_ATOMIC (type) = (type_quals & TYPE_QUAL_ATOMIC) != 0;
+  TYPE_UNTRUSTED (type) = (type_quals & TYPE_QUAL_UNTRUSTED) != 0;
   TYPE_ADDR_SPACE (type) = DECODE_QUAL_ADDR_SPACE (type_quals);
 }
 
diff --git a/gcc/tree.h b/gcc/tree.h
index f62c00bc870..caab575b210 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2197,6 +2197,10 @@  extern tree vector_element_bits_tree (const_tree);
    the term.  */
 #define TYPE_RESTRICT(NODE) (TYPE_CHECK (NODE)->type_common.restrict_flag)
 
+/* Nonzero in a type considered "untrusted" - values should be treated as
+   under attacker control.  */
+#define TYPE_UNTRUSTED(NODE) (TYPE_CHECK (NODE)->type_common.untrusted_flag)
+
 /* If nonzero, type's name shouldn't be emitted into debug info.  */
 #define TYPE_NAMELESS(NODE) (TYPE_CHECK (NODE)->base.u.bits.nameless_flag)
 
@@ -2221,6 +2225,7 @@  extern tree vector_element_bits_tree (const_tree);
 	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
 	  | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)		\
 	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
+	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)	\
 	  | (ENCODE_QUAL_ADDR_SPACE (TYPE_ADDR_SPACE (NODE)))))
 
 /* The same as TYPE_QUALS without the address space qualifications.  */
@@ -2228,14 +2233,16 @@  extern tree vector_element_bits_tree (const_tree);
   ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)		\
 	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
 	  | (TYPE_ATOMIC (NODE) * TYPE_QUAL_ATOMIC)		\
-	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
+	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
+	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
 
 /* The same as TYPE_QUALS without the address space and atomic 
    qualifications.  */
 #define TYPE_QUALS_NO_ADDR_SPACE_NO_ATOMIC(NODE)		\
   ((int) ((TYPE_READONLY (NODE) * TYPE_QUAL_CONST)		\
 	  | (TYPE_VOLATILE (NODE) * TYPE_QUAL_VOLATILE)		\
-	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)))
+	  | (TYPE_RESTRICT (NODE) * TYPE_QUAL_RESTRICT)		\
+	  | (TYPE_UNTRUSTED (NODE) * TYPE_QUAL_UNTRUSTED)))
 
 /* These flags are available for each language front end to use internally.  */
 #define TYPE_LANG_FLAG_0(NODE) (TYPE_CHECK (NODE)->type_common.lang_flag_0)