From patchwork Sat Nov 13 20:37:26 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 47620 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D67C43857C44 for ; Sat, 13 Nov 2021 20:39:16 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D67C43857C44 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1636835956; bh=rhjCSczrZJMqUDYb8yIK1gf/bpiIClGhk2AOG9cWchs=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Dn0EVVU+j7PrN7cLLmEgUqPx565E03mHbOkZLprFmrZ3mSrCtdhgDvhsOhDdzD1UO 1JkYAHJ2EEvKjCMgqRNP9OdcvgE0pfOFoQOrDw0jeFR5bhC/KJwzj7zeM8aBTjJK9+ HaVaDygb2uyWqfOLb2vh3kZtkHdZ0YfiAGtk4ZI8= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 5619A3858403 for ; Sat, 13 Nov 2021 20:37:43 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5619A3858403 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-410-XIk7Ck9bMfKZnkKsmbYQrw-1; Sat, 13 Nov 2021 15:37:41 -0500 X-MC-Unique: XIk7Ck9bMfKZnkKsmbYQrw-1 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id C3189102C887; Sat, 13 Nov 2021 20:37:40 +0000 (UTC) Received: from t14s.localdomain.com (ovpn-113-54.phx2.redhat.com [10.3.113.54]) by smtp.corp.redhat.com (Postfix) with ESMTP id 5657319D9D; Sat, 13 Nov 2021 20:37:40 +0000 (UTC) To: gcc-patches@gcc.gnu.org, linux-toolchains@vger.kernel.org Subject: [PATCH 1b/6] Add __attribute__((untrusted)) Date: Sat, 13 Nov 2021 15:37:26 -0500 Message-Id: <20211113203732.2098220-3-dmalcolm@redhat.com> In-Reply-To: <20211113203732.2098220-1-dmalcolm@redhat.com> References: <20211113203732.2098220-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 on 10.5.11.23 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.4 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: David Malcolm via Gcc-patches From: David Malcolm Reply-To: David Malcolm Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" 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 --- 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)