From 90f2a111413a6d4264335046d68ffa19725864b6 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Wed, 15 Jun 2022 22:51:52 +0200 Subject: [PATCH] d: Add `@no_sanitize' attribute to compiler and library. The `@no_sanitize' attribute disables a particular sanitizer for this function, analogous to `__attribute__((no_sanitize))'. The library also defines `@noSanitize' to be compatible with the LLVM D compiler's `ldc.attributes'. gcc/d/ChangeLog: * d-attribs.cc (d_langhook_attribute_table): Add no_sanitize. (d_handle_no_sanitize_attribute): New function. libphobos/ChangeLog: * libdruntime/gcc/attributes.d (no_sanitize): Define. (noSanitize): Define. gcc/testsuite/ChangeLog: * gdc.dg/asan/attr_no_sanitize1.d: New test. * gdc.dg/ubsan/attr_no_sanitize2.d: New test. --- gcc/d/d-attribs.cc | 52 ++++++++++++++++++++++++++ gcc/testsuite/gdc.dg/asan/attr_no_sanitize1.d | 32 ++++++++++++++++ gcc/testsuite/gdc.dg/ubsan/attr_no_sanitize2.d | 39 +++++++++++++++++++ libphobos/libdruntime/gcc/attributes.d | 35 +++++++++++++++++ 4 files changed, 158 insertions(+) create mode 100644 gcc/testsuite/gdc.dg/asan/attr_no_sanitize1.d create mode 100644 gcc/testsuite/gdc.dg/ubsan/attr_no_sanitize2.d diff --git a/gcc/d/d-attribs.cc b/gcc/d/d-attribs.cc index 4c6e7a7..4b54426 100644 --- a/gcc/d/d-attribs.cc +++ b/gcc/d/d-attribs.cc @@ -78,6 +78,7 @@ static tree d_handle_cold_attribute (tree *, tree, tree, int, bool *); static tree d_handle_restrict_attribute (tree *, tree, tree, int, bool *); static tree d_handle_used_attribute (tree *, tree, tree, int, bool *); static tree d_handle_visibility_attribute (tree *, tree, tree, int, bool *); +static tree d_handle_no_sanitize_attribute (tree *, tree, tree, int, bool *); /* Helper to define attribute exclusions. */ #define ATTR_EXCL(name, function, type, variable) \ @@ -220,6 +221,8 @@ const attribute_spec d_langhook_attribute_table[] = d_handle_alloc_size_attribute, attr_alloc_exclusions), ATTR_SPEC ("cold", 0, 0, true, false, false, false, d_handle_cold_attribute, attr_cold_hot_exclusions), + ATTR_SPEC ("no_sanitize", 1, -1, true, false, false, false, + d_handle_no_sanitize_attribute, NULL), ATTR_SPEC ("restrict", 0, 0, true, false, false, false, d_handle_restrict_attribute, NULL), ATTR_SPEC ("used", 0, 0, true, false, false, false, @@ -1364,6 +1367,55 @@ d_handle_cold_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) return NULL_TREE; } +/* Handle a "no_sanitize" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +d_handle_no_sanitize_attribute (tree *node, tree name, tree args, int, + bool *no_add_attrs) +{ + *no_add_attrs = true; + + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + return NULL_TREE; + } + + unsigned int flags = 0; + for (; args; args = TREE_CHAIN (args)) + { + tree id = TREE_VALUE (args); + if (TREE_CODE (id) != STRING_CST) + { + error ("%qE argument not a string", name); + return NULL_TREE; + } + + char *string = ASTRDUP (TREE_STRING_POINTER (id)); + flags |= parse_no_sanitize_attribute (string); + } + + /* Store the flags argument back into no_sanitize attribute as an integer, + merge existing flags if no_sanitize was previously handled. */ + if (tree attr = lookup_attribute ("no_sanitize", DECL_ATTRIBUTES (*node))) + { + unsigned int old_value = tree_to_uhwi (TREE_VALUE (attr)); + flags |= old_value; + + if (flags != old_value) + TREE_VALUE (attr) = build_int_cst (d_uint_type, flags); + } + else + { + DECL_ATTRIBUTES (*node) = tree_cons (get_identifier ("no_sanitize"), + build_int_cst (d_uint_type, flags), + DECL_ATTRIBUTES (*node)); + } + + return NULL_TREE; +} + /* Handle a "restrict" attribute; arguments as in struct attribute_spec.handler. */ diff --git a/gcc/testsuite/gdc.dg/asan/attr_no_sanitize1.d b/gcc/testsuite/gdc.dg/asan/attr_no_sanitize1.d new file mode 100644 index 0000000..28908d4 --- /dev/null +++ b/gcc/testsuite/gdc.dg/asan/attr_no_sanitize1.d @@ -0,0 +1,32 @@ +// { dg-options "-fsanitize=address -O3 -fdump-tree-optimized" } +// { dg-do compile } + +import gcc.attributes; + +@no_sanitize("address") +__gshared int globalvar1; // { dg-warning "attribute ignored" } + +pragma(inline, true) +@no_sanitize("address") +void test_no_address() +{ + counter++; +} + +pragma(inline, true) +void test_sanitize()() +{ + counter++; +} + +void func1() +{ + counter++; + test_no_address(); + test_sanitize(); +} + +private int counter; + +// { dg-final { scan-tree-dump-times "Function test_no_address" 1 "optimized" } } +// { dg-final { scan-tree-dump-times "Function test_sanitize" 0 "optimized" } } diff --git a/gcc/testsuite/gdc.dg/ubsan/attr_no_sanitize2.d b/gcc/testsuite/gdc.dg/ubsan/attr_no_sanitize2.d new file mode 100644 index 0000000..c6e4784 --- /dev/null +++ b/gcc/testsuite/gdc.dg/ubsan/attr_no_sanitize2.d @@ -0,0 +1,39 @@ +// { dg-options "-fsanitize=undefined -O3 -fdump-tree-optimized" } +// { dg-do compile } + +import gcc.attributes; + +@no_sanitize("invalid_name") +void func1() { } // { dg-warning "attribute directive ignored" } + +@no_sanitize("address") +@no_sanitize("thread") +@no_sanitize("address,thread") +@no_sanitize("address", "undefined") +@no_sanitize("undefined", "leak", "return,null,bounds") +void func2() { } + +pragma(inline, true) +@no_sanitize("undefined") +void test_no_undefined()() +{ + counter++; +} + +pragma(inline, true) +void test_sanitize()() +{ + counter++; +} + +void func3() +{ + counter++; + test_no_undefined(); + test_sanitize(); +} + +private __gshared int counter; + +// { dg-final { scan-tree-dump-times "Function test_no_undefined" 1 "optimized" } } +// { dg-final { scan-tree-dump-times "Function test_sanitize" 0 "optimized" } } diff --git a/libphobos/libdruntime/gcc/attributes.d b/libphobos/libdruntime/gcc/attributes.d index ca066ce..710e8ab 100644 --- a/libphobos/libdruntime/gcc/attributes.d +++ b/libphobos/libdruntime/gcc/attributes.d @@ -182,6 +182,33 @@ enum flatten = attribute("flatten"); enum no_icf = attribute("no_icf"); /** + * The `@no_sanitize` attribute on functions is used to inform the compiler + * that it should not do sanitization of any option mentioned in + * sanitize_option. A list of values acceptable by the `-fsanitize` option + * can be provided. + * + * Example: + * --- + * import gcc.attributes; + * + * @no_sanitize("alignment", "object-size") void func1() { } + * @no_sanitize("alignment,object-size") void func2() { } + * --- + */ + +auto no_sanitize(A...)(A arguments) + if (allSatisfy!(isStringValue, arguments)) +{ + return attribute("no_sanitize", arguments); +} + +auto no_sanitize(A...)(A arguments) + if (!allSatisfy!(isStringValue, arguments)) +{ + assert(false, "no_sanitize attribute argument not a string constant"); +} + +/** * The `@noclone` attribute prevents a function from being considered for * cloning - a mechanism that produces specialized copies of functions and * which is (currently) performed by interprocedural constant propagation. @@ -595,6 +622,14 @@ enum hidden = visibility("hidden"); enum naked = attribute("naked"); /** + * Disables a particular sanitizer for this function. + * Valid sanitizer names are all names accepted by `-fsanitize=` commandline option. + * Multiple sanitizers can be disabled by applying this UDA multiple times, e.g. + * `@noSanitize("address") `@noSanitize("thread")` to disable both ASan and TSan. + */ +alias noSanitize = no_sanitize; + +/** * Sets the optimization strategy for a function. * Valid strategies are "none", "optsize", "minsize". The strategies are * mutually exclusive. -- 2.7.4