On Tue, 2022-01-11 at 23:36 -0500, Jason Merrill wrote: > On 1/10/22 16:36, David Malcolm via Gcc-patches wrote: > > On Thu, 2022-01-06 at 09:08 -0500, David Malcolm wrote: > > > On Sat, 2021-11-13 at 15:37 -0500, David Malcolm wrote: > > > > This patch adds a new __attribute__ ((tainted)) to the C/C++ > > > > frontends. > > > > > > Ping for GCC C/C++ mantainers for review of the C/C++ FE parts of > > > this > > > patch (attribute registration, documentation, the name of the > > > attribute, etc). > > > > > > (I believe it's independent of the rest of the patch kit, in that > > > it > > > could go into trunk without needing the prior patches) > > > > > > Thanks > > > Dave > > > > Getting close to end of stage 3 for GCC 12, so pinging this patch > > again... > > > > https://gcc.gnu.org/pipermail/gcc-patches/2021-November/584376.html > > The c-family change is OK.
Thanks. I'm retesting the patch now, but it now seems to me that __attribute__((tainted_args)) would lead to more readable code than: __attribute__((tainted)) in that the name "tainted_args" better conveys the idea that all arguments are under attacker-control (as opposed to the body of the function or the function pointer being under attacker-control). Looking at https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html we already have some attributes with underscores in their names. Does this sound good? Dave > > > Thanks > > Dave > > > > > > > > > > > > > > > > It can be used on function decls: the analyzer will treat as > > > > tainted > > > > all parameters to the function and all buffers pointed to by > > > > parameters > > > > to the function. Adding this in one place to the Linux kernel's > > > > __SYSCALL_DEFINEx macro allows the analyzer to treat all syscalls > > > > as > > > > having tainted inputs. This gives additional testing beyond e.g. > > > > __user > > > > pointers added by earlier patches - an example of the use of this > > > > can > > > > be > > > > seen in CVE-2011-2210, where given: > > > > > > > > SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user > > > > *, > > > > buffer, > > > > unsigned long, nbytes, int __user *, start, > > > > void > > > > __user *, arg) > > > > > > > > the analyzer will treat the nbytes param as under attacker > > > > control, > > > > and > > > > can complain accordingly: > > > > > > > > taint-CVE-2011-2210-1.c: In function ‘sys_osf_getsysinfo’: > > > > taint-CVE-2011-2210-1.c:69:21: warning: use of attacker- > > > > controlled > > > > value > > > > ‘nbytes’ as size without upper-bounds checking [CWE-129] [- > > > > Wanalyzer-tainted-size] > > > > 69 | if (copy_to_user(buffer, hwrpb, nbytes) > > > > != 0) > > > > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > > > > > > > > Additionally, the patch allows the attribute to be used on field > > > > decls: > > > > specifically function pointers. Any function used as an > > > > initializer > > > > for such a field gets treated as tainted. An example can be seen > > > > in > > > > CVE-2020-13143, where adding __attribute__((tainted)) to the > > > > "store" > > > > callback of configfs_attribute: > > > > > > > > struct configfs_attribute { > > > > /* [...snip...] */ > > > > ssize_t (*store)(struct config_item *, const char *, > > > > size_t) > > > > __attribute__((tainted)); > > > > /* [...snip...] */ > > > > }; > > > > > > > > allows the analyzer to see: > > > > > > > > CONFIGFS_ATTR(gadget_dev_desc_, UDC); > > > > > > > > and treat gadget_dev_desc_UDC_store as tainted, so that it > > > > complains: > > > > > > > > taint-CVE-2020-13143-1.c: In function > > > > ‘gadget_dev_desc_UDC_store’: > > > > taint-CVE-2020-13143-1.c:33:17: warning: use of attacker- > > > > controlled > > > > value > > > > ‘len + 18446744073709551615’ as offset without upper-bounds > > > > checking [CWE-823] [-Wanalyzer-tainted-offset] > > > > 33 | if (name[len - 1] == '\n') > > > > | ~~~~^~~~~~~~~ > > > > > > > > Similarly, the attribute could be used on the ioctl callback > > > > field, > > > > USB device callbacks, network-handling callbacks etc. This > > > > potentially > > > > gives a lot of test coverage with relatively little code > > > > annotation, > > > > and > > > > without necessarily needing link-time analysis (which -fanalyzer > > > > can > > > > only do at present on trivial examples). > > > > > > > > I believe this is the first time we've had an attribute on a > > > > field. > > > > If that's an issue, I could prepare a version of the patch that > > > > merely allowed it on functions themselves. > > > > > > > > As before this currently still needs -fanalyzer-checker=taint (in > > > > addition to -fanalyzer). > > > > > > > > gcc/analyzer/ChangeLog: > > > > * engine.cc: Include "stringpool.h", "attribs.h", and > > > > "tree-dfa.h". > > > > (mark_params_as_tainted): New. > > > > (class tainted_function_custom_event): New. > > > > (class tainted_function_info): New. > > > > (exploded_graph::add_function_entry): Handle functions > > > > with > > > > "tainted" attribute. > > > > (class tainted_field_custom_event): New. > > > > (class tainted_callback_custom_event): New. > > > > (class tainted_call_info): New. > > > > (add_tainted_callback): New. > > > > (add_any_callbacks): New. > > > > (exploded_graph::build_initial_worklist): Find callbacks > > > > that > > > > are > > > > reachable from global initializers, calling > > > > add_any_callbacks > > > > on > > > > them. > > > > > > > > gcc/c-family/ChangeLog: > > > > * c-attribs.c (c_common_attribute_table): Add "tainted". > > > > (handle_tainted_attribute): New. > > > > > > > > gcc/ChangeLog: > > > > * doc/extend.texi (Function Attributes): Note that > > > > "tainted" > > > > can > > > > be used on field decls. > > > > (Common Function Attributes): Add entry on "tainted" > > > > attribute. > > > > > > > > gcc/testsuite/ChangeLog: > > > > * gcc.dg/analyzer/attr-tainted-1.c: New test. > > > > * gcc.dg/analyzer/attr-tainted-misuses.c: New test. > > > > * gcc.dg/analyzer/taint-CVE-2011-2210-1.c: New test. > > > > * gcc.dg/analyzer/taint-CVE-2020-13143-1.c: New test. > > > > * gcc.dg/analyzer/taint-CVE-2020-13143-2.c: New test. > > > > * gcc.dg/analyzer/taint-CVE-2020-13143.h: New test. > > > > * gcc.dg/analyzer/taint-alloc-3.c: New test. > > > > * gcc.dg/analyzer/taint-alloc-4.c: New test. > > > > > > > > Signed-off-by: David Malcolm <dmalc...@redhat.com> > > > > --- > > > > gcc/analyzer/engine.cc | 317 > > > > +++++++++++++++++- > > > > gcc/c-family/c-attribs.c | 36 ++ > > > > gcc/doc/extend.texi | 22 +- > > > > .../gcc.dg/analyzer/attr-tainted-1.c | 88 +++++ > > > > .../gcc.dg/analyzer/attr-tainted-misuses.c | 6 + > > > > .../gcc.dg/analyzer/taint-CVE-2011-2210-1.c | 93 +++++ > > > > .../gcc.dg/analyzer/taint-CVE-2020-13143-1.c | 38 +++ > > > > .../gcc.dg/analyzer/taint-CVE-2020-13143-2.c | 32 ++ > > > > .../gcc.dg/analyzer/taint-CVE-2020-13143.h | 91 +++++ > > > > gcc/testsuite/gcc.dg/analyzer/taint-alloc-3.c | 21 ++ > > > > gcc/testsuite/gcc.dg/analyzer/taint-alloc-4.c | 31 ++ > > > > 11 files changed, 772 insertions(+), 3 deletions(-) > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/attr-tainted- > > > > 1.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/attr-tainted- > > > > misuses.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE- > > > > 2011- > > > > 2210-1.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE- > > > > 2020- > > > > 13143-1.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE- > > > > 2020- > > > > 13143-2.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-CVE- > > > > 2020- > > > > 13143.h > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-alloc- > > > > 3.c > > > > create mode 100644 gcc/testsuite/gcc.dg/analyzer/taint-alloc- > > > > 4.c > > > > > > > > diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc > > > > index 096e219392d..5fab41daf93 100644 > > > > --- a/gcc/analyzer/engine.cc > > > > +++ b/gcc/analyzer/engine.cc > > > > @@ -68,6 +68,9 @@ along with GCC; see the file COPYING3. If not > > > > see > > > > #include "plugin.h" > > > > #include "target.h" > > > > #include <memory> > > > > +#include "stringpool.h" > > > > +#include "attribs.h" > > > > +#include "tree-dfa.h" > > > > > > > > /* For an overview, see gcc/doc/analyzer.texi. */ > > > > > > > > @@ -2276,6 +2279,116 @@ exploded_graph::~exploded_graph () > > > > delete (*iter).second; > > > > } > > > > > > > > +/* Subroutine for use when implementing __attribute__((tainted)) > > > > + on functions and on function pointer fields in structs. > > > > + > > > > + Called on STATE representing a call to FNDECL. > > > > + Mark all params of FNDECL in STATE as "tainted". Mark the > > > > value > > > > of all > > > > + regions pointed to by params of FNDECL as "tainted". > > > > + > > > > + Return true if successful; return false if the "taint" state > > > > machine > > > > + was not found. */ > > > > + > > > > +static bool > > > > +mark_params_as_tainted (program_state *state, tree fndecl, > > > > + const extrinsic_state &ext_state) > > > > +{ > > > > + unsigned taint_sm_idx; > > > > + if (!ext_state.get_sm_idx_by_name ("taint", &taint_sm_idx)) > > > > + return false; > > > > + sm_state_map *smap = state->m_checker_states[taint_sm_idx]; > > > > + > > > > + const state_machine &sm = ext_state.get_sm (taint_sm_idx); > > > > + state_machine::state_t tainted = sm.get_state_by_name > > > > ("tainted"); > > > > + > > > > + region_model_manager *mgr = ext_state.get_model_manager (); > > > > + > > > > + function *fun = DECL_STRUCT_FUNCTION (fndecl); > > > > + gcc_assert (fun); > > > > + > > > > + for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm; > > > > + iter_parm = DECL_CHAIN (iter_parm)) > > > > + { > > > > + tree param = iter_parm; > > > > + if (tree parm_default_ssa = ssa_default_def (fun, > > > > iter_parm)) > > > > + param = parm_default_ssa; > > > > + const region *param_reg = state->m_region_model- > > > > >get_lvalue > > > > (param, NULL); > > > > + const svalue *init_sval = mgr->get_or_create_initial_value > > > > (param_reg); > > > > + smap->set_state (state->m_region_model, init_sval, > > > > + tainted, NULL /*origin_new_sval*/, > > > > ext_state); > > > > + if (POINTER_TYPE_P (TREE_TYPE (param))) > > > > + { > > > > + const region *pointee_reg = mgr->get_symbolic_region > > > > (init_sval); > > > > + /* Mark "*param" as tainted. */ > > > > + const svalue *init_pointee_sval > > > > + = mgr->get_or_create_initial_value (pointee_reg); > > > > + smap->set_state (state->m_region_model, > > > > init_pointee_sval, > > > > + tainted, NULL /*origin_new_sval*/, > > > > ext_state); > > > > + } > > > > + } > > > > + > > > > + return true; > > > > +} > > > > + > > > > +/* Custom event for use by tainted_function_info when a function > > > > + has been marked with __attribute__((tainted)). */ > > > > + > > > > +class tainted_function_custom_event : public custom_event > > > > +{ > > > > +public: > > > > + tainted_function_custom_event (location_t loc, tree fndecl, > > > > int > > > > depth) > > > > + : custom_event (loc, fndecl, depth), > > > > + m_fndecl (fndecl) > > > > + { > > > > + } > > > > + > > > > + label_text get_desc (bool can_colorize) const FINAL OVERRIDE > > > > + { > > > > + return make_label_text > > > > + (can_colorize, > > > > + "function %qE marked with %<__attribute__((tainted))%>", > > > > + m_fndecl); > > > > + } > > > > + > > > > +private: > > > > + tree m_fndecl; > > > > +}; > > > > + > > > > +/* Custom exploded_edge info for top-level calls to a function > > > > + marked with __attribute__((tainted)). */ > > > > + > > > > +class tainted_function_info : public custom_edge_info > > > > +{ > > > > +public: > > > > + tainted_function_info (tree fndecl) > > > > + : m_fndecl (fndecl) > > > > + {} > > > > + > > > > + void print (pretty_printer *pp) const FINAL OVERRIDE > > > > + { > > > > + pp_string (pp, "call to tainted function"); > > > > + }; > > > > + > > > > + bool update_model (region_model *, > > > > + const exploded_edge *, > > > > + region_model_context *) const FINAL OVERRIDE > > > > + { > > > > + /* No-op. */ > > > > + return true; > > > > + } > > > > + > > > > + void add_events_to_path (checker_path *emission_path, > > > > + const exploded_edge &) const FINAL > > > > OVERRIDE > > > > + { > > > > + emission_path->add_event > > > > + (new tainted_function_custom_event > > > > + (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0)); > > > > + } > > > > + > > > > +private: > > > > + tree m_fndecl; > > > > +}; > > > > + > > > > /* Ensure that there is an exploded_node representing an > > > > external > > > > call to > > > > FUN, adding it to the worklist if creating it. > > > > > > > > @@ -2302,14 +2415,25 @@ exploded_graph::add_function_entry > > > > (function > > > > *fun) > > > > program_state state (m_ext_state); > > > > state.push_frame (m_ext_state, fun); > > > > > > > > + custom_edge_info *edge_info = NULL; > > > > + > > > > + if (lookup_attribute ("tainted", DECL_ATTRIBUTES (fun->decl))) > > > > + { > > > > + if (mark_params_as_tainted (&state, fun->decl, > > > > m_ext_state)) > > > > + edge_info = new tainted_function_info (fun->decl); > > > > + } > > > > + > > > > if (!state.m_valid) > > > > return NULL; > > > > > > > > exploded_node *enode = get_or_create_node (point, state, > > > > NULL); > > > > if (!enode) > > > > - return NULL; > > > > + { > > > > + delete edge_info; > > > > + return NULL; > > > > + } > > > > > > > > - add_edge (m_origin, enode, NULL); > > > > + add_edge (m_origin, enode, NULL, edge_info); > > > > > > > > m_functions_with_enodes.add (fun); > > > > > > > > @@ -2623,6 +2747,184 @@ toplevel_function_p (function *fun, > > > > logger > > > > *logger) > > > > return true; > > > > } > > > > > > > > +/* Custom event for use by tainted_call_info when a callback > > > > field > > > > has been > > > > + marked with __attribute__((tainted)), for labelling the > > > > field. > > > > */ > > > > + > > > > +class tainted_field_custom_event : public custom_event > > > > +{ > > > > +public: > > > > + tainted_field_custom_event (tree field) > > > > + : custom_event (DECL_SOURCE_LOCATION (field), NULL_TREE, 0), > > > > + m_field (field) > > > > + { > > > > + } > > > > + > > > > + label_text get_desc (bool can_colorize) const FINAL OVERRIDE > > > > + { > > > > + return make_label_text (can_colorize, > > > > + "field %qE of %qT" > > > > + " is marked with > > > > %<__attribute__((tainted))%>", > > > > + m_field, DECL_CONTEXT (m_field)); > > > > + } > > > > + > > > > +private: > > > > + tree m_field; > > > > +}; > > > > + > > > > +/* Custom event for use by tainted_call_info when a callback > > > > field > > > > has been > > > > + marked with __attribute__((tainted)), for labelling the > > > > function > > > > used > > > > + in that callback. */ > > > > + > > > > +class tainted_callback_custom_event : public custom_event > > > > +{ > > > > +public: > > > > + tainted_callback_custom_event (location_t loc, tree fndecl, > > > > int > > > > depth, > > > > + tree field) > > > > + : custom_event (loc, fndecl, depth), > > > > + m_field (field) > > > > + { > > > > + } > > > > + > > > > + label_text get_desc (bool can_colorize) const FINAL OVERRIDE > > > > + { > > > > + return make_label_text (can_colorize, > > > > + "function %qE used as initializer for > > > > field %qE" > > > > + " marked with > > > > %<__attribute__((tainted))%>", > > > > + m_fndecl, m_field); > > > > + } > > > > + > > > > +private: > > > > + tree m_field; > > > > +}; > > > > + > > > > +/* Custom edge info for use when adding a function used by a > > > > callback field > > > > + marked with '__attribute__((tainted))'. */ > > > > + > > > > +class tainted_call_info : public custom_edge_info > > > > +{ > > > > +public: > > > > + tainted_call_info (tree field, tree fndecl, location_t loc) > > > > + : m_field (field), m_fndecl (fndecl), m_loc (loc) > > > > + {} > > > > + > > > > + void print (pretty_printer *pp) const FINAL OVERRIDE > > > > + { > > > > + pp_string (pp, "call to tainted field"); > > > > + }; > > > > + > > > > + bool update_model (region_model *, > > > > + const exploded_edge *, > > > > + region_model_context *) const FINAL OVERRIDE > > > > + { > > > > + /* No-op. */ > > > > + return true; > > > > + } > > > > + > > > > + void add_events_to_path (checker_path *emission_path, > > > > + const exploded_edge &) const FINAL > > > > OVERRIDE > > > > + { > > > > + /* Show the field in the struct declaration > > > > + e.g. "(1) field 'store' is marked with > > > > '__attribute__((tainted))'" */ > > > > + emission_path->add_event > > > > + (new tainted_field_custom_event (m_field)); > > > > + > > > > + /* Show the callback in the initializer > > > > + e.g. > > > > + "(2) function 'gadget_dev_desc_UDC_store' used as > > > > initializer > > > > + for field 'store' marked with > > > > '__attribute__((tainted))'". > > > > */ > > > > + emission_path->add_event > > > > + (new tainted_callback_custom_event (m_loc, m_fndecl, 0, > > > > m_field)); > > > > + } > > > > + > > > > +private: > > > > + tree m_field; > > > > + tree m_fndecl; > > > > + location_t m_loc; > > > > +}; > > > > + > > > > +/* Given an initializer at LOC for FIELD marked with > > > > '__attribute__((tainted))' > > > > + initialized with FNDECL, add an entrypoint to FNDECL to EG > > > > (and > > > > to its > > > > + worklist) where the params to FNDECL are marked as tainted. > > > > */ > > > > + > > > > +static void > > > > +add_tainted_callback (exploded_graph *eg, tree field, tree > > > > fndecl, > > > > + location_t loc) > > > > +{ > > > > + logger *logger = eg->get_logger (); > > > > + > > > > + LOG_SCOPE (logger); > > > > + > > > > + if (!gimple_has_body_p (fndecl)) > > > > + return; > > > > + > > > > + const extrinsic_state &ext_state = eg->get_ext_state (); > > > > + > > > > + function *fun = DECL_STRUCT_FUNCTION (fndecl); > > > > + gcc_assert (fun); > > > > + > > > > + program_point point > > > > + = program_point::from_function_entry (eg->get_supergraph (), > > > > fun); > > > > + program_state state (ext_state); > > > > + state.push_frame (ext_state, fun); > > > > + > > > > + if (!mark_params_as_tainted (&state, fndecl, ext_state)) > > > > + return; > > > > + > > > > + if (!state.m_valid) > > > > + return; > > > > + > > > > + exploded_node *enode = eg->get_or_create_node (point, state, > > > > NULL); > > > > + if (logger) > > > > + { > > > > + if (enode) > > > > + logger->log ("created EN %i for tainted %qE entrypoint", > > > > + enode->m_index, fndecl); > > > > + else > > > > + { > > > > + logger->log ("did not create enode for tainted %qE > > > > entrypoint", > > > > + fndecl); > > > > + return; > > > > + } > > > > + } > > > > + > > > > + tainted_call_info *info = new tainted_call_info (field, > > > > fndecl, > > > > loc); > > > > + eg->add_edge (eg->get_origin (), enode, NULL, info); > > > > +} > > > > + > > > > +/* Callback for walk_tree for finding callbacks within > > > > initializers; > > > > + ensure that any callback initializer where the corresponding > > > > field is > > > > + marked with '__attribute__((tainted))' is treated as an > > > > entrypoint to the > > > > + analysis, special-casing that the inputs to the callback are > > > > + untrustworthy. */ > > > > + > > > > +static tree > > > > +add_any_callbacks (tree *tp, int *, void *data) > > > > +{ > > > > + exploded_graph *eg = (exploded_graph *)data; > > > > + if (TREE_CODE (*tp) == CONSTRUCTOR) > > > > + { > > > > + /* Find fields with the "tainted" attribute. > > > > + walk_tree only walks the values, not the index values; > > > > + look at the index values. */ > > > > + unsigned HOST_WIDE_INT idx; > > > > + constructor_elt *ce; > > > > + > > > > + for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp), > > > > idx, > > > > &ce); > > > > + idx++) > > > > + if (ce->index && TREE_CODE (ce->index) == FIELD_DECL) > > > > + if (lookup_attribute ("tainted", DECL_ATTRIBUTES (ce- > > > > > index))) > > > > + { > > > > + tree value = ce->value; > > > > + if (TREE_CODE (value) == ADDR_EXPR > > > > + && TREE_CODE (TREE_OPERAND (value, 0)) == > > > > FUNCTION_DECL) > > > > + add_tainted_callback (eg, ce->index, TREE_OPERAND > > > > (value, 0), > > > > + EXPR_LOCATION (value)); > > > > + } > > > > + } > > > > + > > > > + return NULL_TREE; > > > > +} > > > > + > > > > /* Add initial nodes to EG, with entrypoints for externally- > > > > callable > > > > functions. */ > > > > > > > > @@ -2648,6 +2950,17 @@ exploded_graph::build_initial_worklist () > > > > logger->log ("did not create enode for %qE > > > > entrypoint", > > > > fun->decl); > > > > } > > > > } > > > > + > > > > + /* Find callbacks that are reachable from global > > > > initializers. */ > > > > + varpool_node *vpnode; > > > > + FOR_EACH_VARIABLE (vpnode) > > > > + { > > > > + tree decl = vpnode->decl; > > > > + tree init = DECL_INITIAL (decl); > > > > + if (!init) > > > > + continue; > > > > + walk_tree (&init, add_any_callbacks, this, NULL); > > > > + } > > > > } > > > > > > > > /* The main loop of the analysis. > > > > diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c > > > > index 9e03156de5e..835ba6e0e8c 100644 > > > > --- a/gcc/c-family/c-attribs.c > > > > +++ b/gcc/c-family/c-attribs.c > > > > @@ -117,6 +117,7 @@ static tree > > > > handle_no_profile_instrument_function_attribute (tree *, tree, > > > > > > > > tree, > > > > int, bool *); > > > > static tree handle_malloc_attribute (tree *, tree, tree, int, > > > > bool > > > > *); > > > > static tree handle_dealloc_attribute (tree *, tree, tree, int, > > > > bool > > > > *); > > > > +static tree handle_tainted_attribute (tree *, tree, tree, int, > > > > bool > > > > *); > > > > static tree handle_returns_twice_attribute (tree *, tree, tree, > > > > int, > > > > bool *); > > > > static tree handle_no_limit_stack_attribute (tree *, tree, > > > > tree, > > > > int, > > > > bool *); > > > > @@ -569,6 +570,8 @@ const struct attribute_spec > > > > c_common_attribute_table[] = > > > > handle_objc_nullability_attribute, > > > > NULL > > > > }, > > > > { "*dealloc", 1, 2, true, false, false, false, > > > > handle_dealloc_attribute, NULL }, > > > > + { "tainted", 0, 0, true, false, false, false, > > > > + handle_tainted_attribute, NULL }, > > > > { NULL, 0, 0, false, false, false, false, > > > > NULL, NULL } > > > > }; > > > > > > > > @@ -5857,6 +5860,39 @@ handle_objc_nullability_attribute (tree > > > > *node, > > > > tree name, tree args, > > > > return NULL_TREE; > > > > } > > > > > > > > +/* Handle a "tainted" attribute; arguments as in > > > > + struct attribute_spec.handler. */ > > > > + > > > > +static tree > > > > +handle_tainted_attribute (tree *node, tree name, tree, int, > > > > + bool *no_add_attrs) > > > > +{ > > > > + if (TREE_CODE (*node) != FUNCTION_DECL > > > > + && TREE_CODE (*node) != FIELD_DECL) > > > > + { > > > > + warning (OPT_Wattributes, "%qE attribute ignored; valid > > > > only " > > > > + "for functions and function pointer fields", > > > > + name); > > > > + *no_add_attrs = true; > > > > + return NULL_TREE; > > > > + } > > > > + > > > > + if (TREE_CODE (*node) == FIELD_DECL > > > > + && !(TREE_CODE (TREE_TYPE (*node)) == POINTER_TYPE > > > > + && TREE_CODE (TREE_TYPE (TREE_TYPE (*node))) == > > > > FUNCTION_TYPE)) > > > > + { > > > > + warning (OPT_Wattributes, "%qE attribute ignored;" > > > > + " field must be a function pointer", > > > > + name); > > > > + *no_add_attrs = true; > > > > + return NULL_TREE; > > > > + } > > > > + > > > > + *no_add_attrs = false; /* OK */ > > > > + > > > > + return NULL_TREE; > > > > +} > > > > + > > > > /* Attempt to partially validate a single attribute ATTR as if > > > > it were to be applied to an entity OPER. */ > > > > > > > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > > > > index 5a6ef464779..826bbd48e7e 100644 > > > > --- a/gcc/doc/extend.texi > > > > +++ b/gcc/doc/extend.texi > > > > @@ -2465,7 +2465,8 @@ variable declarations (@pxref{Variable > > > > Attributes}), > > > > labels (@pxref{Label Attributes}), > > > > enumerators (@pxref{Enumerator Attributes}), > > > > statements (@pxref{Statement Attributes}), > > > > -and types (@pxref{Type Attributes}). > > > > +types (@pxref{Type Attributes}), > > > > +and on field declarations (for @code{tainted}). > > > > > > > > There is some overlap between the purposes of attributes and > > > > pragmas > > > > (@pxref{Pragmas,,Pragmas Accepted by GCC}). It has been > > > > @@ -3977,6 +3978,25 @@ addition to creating a symbol version (as > > > > if > > > > @code{"@var{name2}@@@var{nodename}"} was used) the version will > > > > be > > > > also used > > > > to resolve @var{name2} by the linker. > > > > > > > > +@item tainted > > > > +@cindex @code{tainted} function attribute > > > > +The @code{tainted} attribute is used to specify that a function > > > > is > > > > called > > > > +in a way that requires sanitization of its arguments, such as a > > > > system > > > > +call in an operating system kernel. Such a function can be > > > > considered part > > > > +of the ``attack surface'' of the program. The attribute can be > > > > used > > > > both > > > > +on function declarations, and on field declarations containing > > > > function > > > > +pointers. In the latter case, any function used as an > > > > initializer > > > > of > > > > +such a callback field will be treated as tainted. > > > > + > > > > +The analyzer will pay particular attention to such functions > > > > when > > > > both > > > > +@option{-fanalyzer} and @option{-fanalyzer-checker=taint} are > > > > supplied, > > > > +potentially issuing warnings guarded by > > > > +@option{-Wanalyzer-exposure-through-uninit-copy}, > > > > +@option{-Wanalyzer-tainted-allocation-size}, > > > > +@option{-Wanalyzer-tainted-array-index}, > > > > +@option{Wanalyzer-tainted-offset}, > > > > +and @option{Wanalyzer-tainted-size}. > > > > + > > > > @item target_clones (@var{options}) > > > > @cindex @code{target_clones} function attribute > > > > The @code{target_clones} attribute is used to specify that a > > > > function > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-tainted-1.c > > > > b/gcc/testsuite/gcc.dg/analyzer/attr-tainted-1.c > > > > new file mode 100644 > > > > index 00000000000..cc4d5900372 > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/attr-tainted-1.c > > > > @@ -0,0 +1,88 @@ > > > > +// TODO: remove need for this option > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +#include "analyzer-decls.h" > > > > + > > > > +struct arg_buf > > > > +{ > > > > + int i; > > > > + int j; > > > > +}; > > > > + > > > > +/* Example of marking a function as tainted. */ > > > > + > > > > +void __attribute__((tainted)) > > > > +test_1 (int i, void *p, char *q) > > > > +{ > > > > + /* There should be a single enode, > > > > + for the "tainted" entry to the function. */ > > > > + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 > > > > processed > > > > enode" } */ > > > > + > > > > + __analyzer_dump_state ("taint", i); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", p); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", q); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", *q); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + > > > > + struct arg_buf *args = p; > > > > + __analyzer_dump_state ("taint", args->i); /* { dg-warning > > > > "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", args->j); /* { dg-warning > > > > "state: > > > > 'tainted'" } */ > > > > +} > > > > + > > > > +/* Example of marking a callback field as tainted. */ > > > > + > > > > +struct s2 > > > > +{ > > > > + void (*cb) (int, void *, char *) > > > > + __attribute__((tainted)); > > > > +}; > > > > + > > > > +/* Function not marked as tainted. */ > > > > + > > > > +void > > > > +test_2a (int i, void *p, char *q) > > > > +{ > > > > + /* There should be a single enode, > > > > + for the normal entry to the function. */ > > > > + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 > > > > processed > > > > enode" } */ > > > > + > > > > + __analyzer_dump_state ("taint", i); /* { dg-warning "state: > > > > 'start'" } */ > > > > + __analyzer_dump_state ("taint", p); /* { dg-warning "state: > > > > 'start'" } */ > > > > + __analyzer_dump_state ("taint", q); /* { dg-warning "state: > > > > 'start'" } */ > > > > + > > > > + struct arg_buf *args = p; > > > > + __analyzer_dump_state ("taint", args->i); /* { dg-warning > > > > "state: > > > > 'start'" } */ > > > > + __analyzer_dump_state ("taint", args->j); /* { dg-warning > > > > "state: > > > > 'start'" } */ > > > > +} > > > > + > > > > +/* Function referenced via t2b.cb, marked as "tainted". */ > > > > + > > > > +void > > > > +test_2b (int i, void *p, char *q) > > > > +{ > > > > + /* There should be two enodes > > > > + for the direct call, and the "tainted" entry to the > > > > function. > > > > */ > > > > + __analyzer_dump_exploded_nodes (0); /* { dg-warning "2 > > > > processed > > > > enodes" } */ > > > > +} > > > > + > > > > +/* Callback used via t2c.cb, marked as "tainted". */ > > > > +void > > > > +__analyzer_test_2c (int i, void *p, char *q) > > > > +{ > > > > + /* There should be a single enode, > > > > + for the "tainted" entry to the function. */ > > > > + __analyzer_dump_exploded_nodes (0); /* { dg-warning "1 > > > > processed > > > > enode" } */ > > > > + > > > > + __analyzer_dump_state ("taint", i); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", p); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", q); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > +} > > > > + > > > > +struct s2 t2b = > > > > +{ > > > > + .cb = test_2b > > > > +}; > > > > + > > > > +struct s2 t2c = > > > > +{ > > > > + .cb = __analyzer_test_2c > > > > +}; > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/attr-tainted-misuses.c > > > > b/gcc/testsuite/gcc.dg/analyzer/attr-tainted-misuses.c > > > > new file mode 100644 > > > > index 00000000000..6f4cbc82efb > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/attr-tainted-misuses.c > > > > @@ -0,0 +1,6 @@ > > > > +int not_a_fn __attribute__ ((tainted)); /* { dg-warning > > > > "'tainted' > > > > attribute ignored; valid only for functions and function pointer > > > > fields" } */ > > > > + > > > > +struct s > > > > +{ > > > > + int f __attribute__ ((tainted)); /* { dg-warning "'tainted' > > > > attribute ignored; field must be a function pointer" } */ > > > > +}; > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-2210- > > > > 1.c > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-2210-1.c > > > > new file mode 100644 > > > > index 00000000000..fe6c7ebbb1f > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2011-2210-1.c > > > > @@ -0,0 +1,93 @@ > > > > +/* "The osf_getsysinfo function in arch/alpha/kernel/osf_sys.c > > > > in > > > > the > > > > + Linux kernel before 2.6.39.4 on the Alpha platform does not > > > > properly > > > > + restrict the data size for GSI_GET_HWRPB operations, which > > > > allows > > > > + local users to obtain sensitive information from kernel > > > > memory > > > > via > > > > + a crafted call." > > > > + > > > > + Fixed in 3d0475119d8722798db5e88f26493f6547a4bb5b on linux- > > > > 2.6.39.y > > > > + in linux-stable. */ > > > > + > > > > +// TODO: remove need for this option: > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +#include "analyzer-decls.h" > > > > +#include "test-uaccess.h" > > > > + > > > > +/* Adapted from include/linux/linkage.h. */ > > > > + > > > > +#define asmlinkage > > > > + > > > > +/* Adapted from include/linux/syscalls.h. */ > > > > + > > > > +#define __SC_DECL1(t1, a1) t1 a1 > > > > +#define __SC_DECL2(t2, a2, ...) t2 a2, __SC_DECL1(__VA_ARGS__) > > > > +#define __SC_DECL3(t3, a3, ...) t3 a3, __SC_DECL2(__VA_ARGS__) > > > > +#define __SC_DECL4(t4, a4, ...) t4 a4, __SC_DECL3(__VA_ARGS__) > > > > +#define __SC_DECL5(t5, a5, ...) t5 a5, __SC_DECL4(__VA_ARGS__) > > > > +#define __SC_DECL6(t6, a6, ...) t6 a6, __SC_DECL5(__VA_ARGS__) > > > > + > > > > +#define SYSCALL_DEFINEx(x, sname, ...) \ > > > > + __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) > > > > + > > > > +#define SYSCALL_DEFINE(name) asmlinkage long sys_##name > > > > +#define __SYSCALL_DEFINEx(x, name, > > > > ...) \ > > > > + asmlinkage __attribute__((tainted)) \ > > > > + long sys##name(__SC_DECL##x(__VA_ARGS__)) > > > > + > > > > +#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, > > > > __VA_ARGS__) > > > > + > > > > +/* Adapted from arch/alpha/include/asm/hwrpb.h. */ > > > > + > > > > +struct hwrpb_struct { > > > > + unsigned long phys_addr; /* check: physical > > > > address of > > > > the hwrpb */ > > > > + unsigned long id; /* check: "HWRPB\0\0\0" > > > > */ > > > > + unsigned long revision; > > > > + unsigned long size; /* size of hwrpb */ > > > > + /* [...snip...] */ > > > > +}; > > > > + > > > > +extern struct hwrpb_struct *hwrpb; > > > > + > > > > +/* Adapted from arch/alpha/kernel/osf_sys.c. */ > > > > + > > > > +SYSCALL_DEFINE5(osf_getsysinfo, unsigned long, op, void __user > > > > *, > > > > buffer, > > > > + unsigned long, nbytes, int __user *, start, void > > > > __user *, arg) > > > > +{ > > > > + /* [...snip...] */ > > > > + > > > > + __analyzer_dump_state ("taint", nbytes); /* { dg-warning > > > > "tainted" } */ > > > > + > > > > + /* TODO: should have an event explaining why "nbytes" is > > > > treated as > > > > + attacker-controlled. */ > > > > + > > > > + /* case GSI_GET_HWRPB: */ > > > > + if (nbytes < sizeof(*hwrpb)) > > > > + return -1; > > > > + > > > > + __analyzer_dump_state ("taint", nbytes); /* { > > > > dg- > > > > warning "has_lb" } */ > > > > + > > > > + if (copy_to_user(buffer, hwrpb, nbytes) != 0) /* > > > > { > > > > dg-warning "use of attacker-controlled value 'nbytes' as size > > > > without > > > > upper-bounds checking" } */ > > > > + return -2; > > > > + > > > > + return 1; > > > > + > > > > + /* [...snip...] */ > > > > +} > > > > + > > > > +/* With the fix for the sense of the size comparison. */ > > > > + > > > > +SYSCALL_DEFINE5(osf_getsysinfo_fixed, unsigned long, op, void > > > > __user > > > > *, buffer, > > > > + unsigned long, nbytes, int __user *, start, void > > > > __user *, arg) > > > > +{ > > > > + /* [...snip...] */ > > > > + > > > > + /* case GSI_GET_HWRPB: */ > > > > + if (nbytes > sizeof(*hwrpb)) > > > > + return -1; > > > > + if (copy_to_user(buffer, hwrpb, nbytes) != 0) /* > > > > { > > > > dg-bogus "attacker-controlled" } */ > > > > + return -2; > > > > + > > > > + return 1; > > > > + > > > > + /* [...snip...] */ > > > > +} > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143- > > > > 1.c > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143-1.c > > > > new file mode 100644 > > > > index 00000000000..0b9a94a8d6c > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143-1.c > > > > @@ -0,0 +1,38 @@ > > > > +/* See notes in this header. */ > > > > +#include "taint-CVE-2020-13143.h" > > > > + > > > > +// TODO: remove need for this option > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +struct configfs_attribute { > > > > + /* [...snip...] */ > > > > + ssize_t (*store)(struct config_item *, const char *, > > > > size_t) > > > > /* { dg-message "\\(1\\) field 'store' of 'struct > > > > configfs_attribute' > > > > is marked with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > + __attribute__((tainted)); /* (this is added). */ > > > > +}; > > > > +static inline struct gadget_info *to_gadget_info(struct > > > > config_item > > > > *item) > > > > +{ > > > > + return container_of(to_config_group(item), struct > > > > gadget_info, group); > > > > +} > > > > + > > > > +static ssize_t gadget_dev_desc_UDC_store(struct config_item > > > > *item, > > > > + const char *page, size_t len) > > > > +{ > > > > + struct gadget_info *gi = to_gadget_info(item); > > > > + char *name; > > > > + int ret; > > > > + > > > > +#if 0 > > > > + /* FIXME: this is the fix. */ > > > > + if (strlen(page) < len) > > > > + return -EOVERFLOW; > > > > +#endif > > > > + > > > > + name = kstrdup(page, GFP_KERNEL); > > > > + if (!name) > > > > + return -ENOMEM; > > > > + if (name[len - 1] == '\n') /* { dg-warning "use of > > > > attacker- > > > > controlled value 'len \[^\n\r\]+' as offset without upper-bounds > > > > checking" } */ > > > > + name[len - 1] = '\0'; /* { dg-warning "use of > > > > attacker-controlled value 'len \[^\n\r\]+' as offset without > > > > upper- > > > > bounds checking" } */ > > > > + /* [...snip...] */ \ > > > > +} > > > > + > > > > +CONFIGFS_ATTR(gadget_dev_desc_, UDC); /* { dg-message "\\(2\\) > > > > function 'gadget_dev_desc_UDC_store' used as initializer for > > > > field > > > > 'store' marked with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143- > > > > 2.c > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143-2.c > > > > new file mode 100644 > > > > index 00000000000..e05da9276c1 > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143-2.c > > > > @@ -0,0 +1,32 @@ > > > > +/* See notes in this header. */ > > > > +#include "taint-CVE-2020-13143.h" > > > > + > > > > +// TODO: remove need for this option > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +struct configfs_attribute { > > > > + /* [...snip...] */ > > > > + ssize_t (*store)(struct config_item *, const char *, > > > > size_t) > > > > /* { dg-message "\\(1\\) field 'store' of 'struct > > > > configfs_attribute' > > > > is marked with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > + __attribute__((tainted)); /* (this is added). */ > > > > +}; > > > > + > > > > +/* Highly simplified version. */ > > > > + > > > > +static ssize_t gadget_dev_desc_UDC_store(struct config_item > > > > *item, > > > > + const char *page, size_t len) > > > > +{ > > > > + /* TODO: ought to have state_change_event talking about > > > > where > > > > the tainted value comes from. */ > > > > + > > > > + char *name; > > > > + /* [...snip...] */ > > > > + > > > > + name = kstrdup(page, GFP_KERNEL); > > > > + if (!name) > > > > + return -ENOMEM; > > > > + if (name[len - 1] == '\n') /* { dg-warning "use of > > > > attacker- > > > > controlled value 'len \[^\n\r\]+' as offset without upper-bounds > > > > checking" } */ > > > > + name[len - 1] = '\0'; /* { dg-warning "use of > > > > attacker-controlled value 'len \[^\n\r\]+' as offset without > > > > upper- > > > > bounds checking" } */ > > > > + /* [...snip...] */ > > > > + return 0; > > > > +} > > > > + > > > > +CONFIGFS_ATTR(gadget_dev_desc_, UDC); /* { dg-message "\\(2\\) > > > > function 'gadget_dev_desc_UDC_store' used as initializer for > > > > field > > > > 'store' marked with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143.h > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143.h > > > > new file mode 100644 > > > > index 00000000000..0ba023539af > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-CVE-2020-13143.h > > > > @@ -0,0 +1,91 @@ > > > > +/* Shared header for the various taint-CVE-2020-13143.h tests. > > > > + > > > > + "gadget_dev_desc_UDC_store in drivers/usb/gadget/configfs.c > > > > in > > > > the > > > > + Linux kernel 3.16 through 5.6.13 relies on kstrdup without > > > > considering > > > > + the possibility of an internal '\0' value, which allows > > > > attackers > > > > to > > > > + trigger an out-of-bounds read, aka CID-15753588bcd4." > > > > + > > > > + Fixed by 15753588bcd4bbffae1cca33c8ced5722477fe1f on linux- > > > > 5.7.y > > > > + in linux-stable. */ > > > > + > > > > +// TODO: remove need for this option > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +#include <stddef.h> > > > > + > > > > +/* Adapted from include/uapi/asm-generic/posix_types.h */ > > > > + > > > > +typedef unsigned int __kernel_size_t; > > > > +typedef int __kernel_ssize_t; > > > > + > > > > +/* Adapted from include/linux/types.h */ > > > > + > > > > +//typedef __kernel_size_t size_t; > > > > +typedef __kernel_ssize_t ssize_t; > > > > + > > > > +/* Adapted from include/linux/kernel.h */ > > > > + > > > > +#define container_of(ptr, type, member) > > > > ({ \ > > > > + void *__mptr = (void > > > > *)(ptr); \ > > > > + /* [...snip...] > > > > */ \ > > > > + ((type *)(__mptr - offsetof(type, member))); }) > > > > + > > > > +/* Adapted from include/linux/configfs.h */ > > > > + > > > > +struct config_item { > > > > + /* [...snip...] */ > > > > +}; > > > > + > > > > +struct config_group { > > > > + struct config_item cg_item; > > > > + /* [...snip...] */ > > > > +}; > > > > + > > > > +static inline struct config_group *to_config_group(struct > > > > config_item *item) > > > > +{ > > > > + return item ? container_of(item,struct > > > > config_group,cg_item) > > > > : NULL; > > > > +} > > > > + > > > > +#define CONFIGFS_ATTR(_pfx, _name) \ > > > > +static struct configfs_attribute _pfx##attr_##_name = { \ > > > > + /* [...snip...] */ \ > > > > + .store = _pfx##_name##_store, \ > > > > +} > > > > + > > > > +/* Adapted from include/linux/compiler.h */ > > > > + > > > > +#define __force > > > > + > > > > +/* Adapted from include/asm-generic/errno-base.h */ > > > > + > > > > +#define ENOMEM 12 /* Out of memory */ > > > > + > > > > +/* Adapted from include/linux/types.h */ > > > > + > > > > +#define __bitwise__ > > > > +typedef unsigned __bitwise__ gfp_t; > > > > + > > > > +/* Adapted from include/linux/gfp.h */ > > > > + > > > > +#define ___GFP_WAIT 0x10u > > > > +#define ___GFP_IO 0x40u > > > > +#define ___GFP_FS 0x80u > > > > +#define __GFP_WAIT ((__force gfp_t)___GFP_WAIT) > > > > +#define __GFP_IO ((__force gfp_t)___GFP_IO) > > > > +#define __GFP_FS ((__force gfp_t)___GFP_FS) > > > > +#define GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS) > > > > + > > > > +/* Adapted from include/linux/compiler_attributes.h */ > > > > + > > > > +#define __malloc > > > > __attribute__((__malloc__)) > > > > + > > > > +/* Adapted from include/linux/string.h */ > > > > + > > > > +extern char *kstrdup(const char *s, gfp_t gfp) __malloc; > > > > + > > > > +/* Adapted from drivers/usb/gadget/configfs.c */ > > > > + > > > > +struct gadget_info { > > > > + struct config_group group; > > > > + /* [...snip...] */ \ > > > > +}; > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-alloc-3.c > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-3.c > > > > new file mode 100644 > > > > index 00000000000..4c567b2ffdf > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-3.c > > > > @@ -0,0 +1,21 @@ > > > > +// TODO: remove need for this option: > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +#include "analyzer-decls.h" > > > > +#include <stdio.h> > > > > +#include <stdlib.h> > > > > +#include <string.h> > > > > + > > > > +/* malloc with tainted size from a syscall. */ > > > > + > > > > +void *p; > > > > + > > > > +void __attribute__((tainted)) > > > > +test_1 (size_t sz) /* { dg-message "\\(1\\) function 'test_1' > > > > marked > > > > with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > +{ > > > > + /* TODO: should have a message saying why "sz" is tainted, > > > > e.g. > > > > + "treating 'sz' as attacker-controlled because 'test_1' is > > > > marked with '__attribute__((tainted))'" */ > > > > + > > > > + p = malloc (sz); /* { dg-warning "use of attacker-controlled > > > > value > > > > 'sz' as allocation size without upper-bounds checking" "warning" > > > > } */ > > > > + /* { dg-message "\\(\[0-9\]+\\) use of attacker-controlled > > > > value > > > > 'sz' as allocation size without upper-bounds checking" "final > > > > event" > > > > { target *-*-* } .-1 } */ > > > > +} > > > > diff --git a/gcc/testsuite/gcc.dg/analyzer/taint-alloc-4.c > > > > b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-4.c > > > > new file mode 100644 > > > > index 00000000000..f52cafcd71d > > > > --- /dev/null > > > > +++ b/gcc/testsuite/gcc.dg/analyzer/taint-alloc-4.c > > > > @@ -0,0 +1,31 @@ > > > > +// TODO: remove need for this option: > > > > +/* { dg-additional-options "-fanalyzer-checker=taint" } */ > > > > + > > > > +#include "analyzer-decls.h" > > > > +#include <stdio.h> > > > > +#include <stdlib.h> > > > > +#include <string.h> > > > > + > > > > +/* malloc with tainted size from a syscall. */ > > > > + > > > > +struct arg_buf > > > > +{ > > > > + size_t sz; > > > > +}; > > > > + > > > > +void *p; > > > > + > > > > +void __attribute__((tainted)) > > > > +test_1 (void *data) /* { dg-message "\\(1\\) function 'test_1' > > > > marked with '__attribute__\\(\\(tainted\\)\\)'" } */ > > > > +{ > > > > + /* we should treat pointed-to-structs as tainted. */ > > > > + __analyzer_dump_state ("taint", data); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + > > > > + struct arg_buf *args = data; > > > > + > > > > + __analyzer_dump_state ("taint", args); /* { dg-warning "state: > > > > 'tainted'" } */ > > > > + __analyzer_dump_state ("taint", args->sz); /* { dg-warning > > > > "state: > > > > 'tainted'" } */ > > > > + > > > > + p = malloc (args->sz); /* { dg-warning "use of attacker- > > > > controlled > > > > value '\\*args.sz' as allocation size without upper-bounds > > > > checking" > > > > "warning" } */ > > > > + /* { dg-message "\\(\[0-9\]+\\) use of attacker-controlled > > > > value > > > > '\\*args.sz' as allocation size without upper-bounds checking" > > > > "final > > > > event" { target *-*-* } .-1 } */ > > > > +} > > > > > > > >