perf parse-events: Fix reading of invalid memory in event parsing

ADD_CONFIG_TERM accesses term->weak, however, in get_config_chgs this
value is accessed outside of the list_for_each_entry and references
invalid memory. Add an argument for ADD_CONFIG_TERM for weak and set it
to false in the get_config_chgs case.

This bug was cause by clang's address sanitizer and libfuzzer. It can be
reproduced with a command line of:

  perf stat -a -e i/bs,tsc,L2/o

Signed-off-by: Ian Rogers <irogers@google.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Leo Yan <leo.yan@linaro.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: clang-built-linux@googlegroups.com
Link: http://lore.kernel.org/lkml/20200307073121.203816-1-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Ian Rogers 2020-03-06 23:31:21 -08:00 committed by Arnaldo Carvalho de Melo
parent a7ffd416d8
commit 05e54e2386

View File

@ -1213,7 +1213,7 @@ static int config_attr(struct perf_event_attr *attr,
static int get_config_terms(struct list_head *head_config, static int get_config_terms(struct list_head *head_config,
struct list_head *head_terms __maybe_unused) struct list_head *head_terms __maybe_unused)
{ {
#define ADD_CONFIG_TERM(__type) \ #define ADD_CONFIG_TERM(__type, __weak) \
struct perf_evsel_config_term *__t; \ struct perf_evsel_config_term *__t; \
\ \
__t = zalloc(sizeof(*__t)); \ __t = zalloc(sizeof(*__t)); \
@ -1222,18 +1222,18 @@ static int get_config_terms(struct list_head *head_config,
\ \
INIT_LIST_HEAD(&__t->list); \ INIT_LIST_HEAD(&__t->list); \
__t->type = PERF_EVSEL__CONFIG_TERM_ ## __type; \ __t->type = PERF_EVSEL__CONFIG_TERM_ ## __type; \
__t->weak = term->weak; \ __t->weak = __weak; \
list_add_tail(&__t->list, head_terms) list_add_tail(&__t->list, head_terms)
#define ADD_CONFIG_TERM_VAL(__type, __name, __val) \ #define ADD_CONFIG_TERM_VAL(__type, __name, __val, __weak) \
do { \ do { \
ADD_CONFIG_TERM(__type); \ ADD_CONFIG_TERM(__type, __weak); \
__t->val.__name = __val; \ __t->val.__name = __val; \
} while (0) } while (0)
#define ADD_CONFIG_TERM_STR(__type, __val) \ #define ADD_CONFIG_TERM_STR(__type, __val, __weak) \
do { \ do { \
ADD_CONFIG_TERM(__type); \ ADD_CONFIG_TERM(__type, __weak); \
__t->val.str = strdup(__val); \ __t->val.str = strdup(__val); \
if (!__t->val.str) { \ if (!__t->val.str) { \
zfree(&__t); \ zfree(&__t); \
@ -1247,62 +1247,62 @@ do { \
list_for_each_entry(term, head_config, list) { list_for_each_entry(term, head_config, list) {
switch (term->type_term) { switch (term->type_term) {
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
ADD_CONFIG_TERM_VAL(PERIOD, period, term->val.num); ADD_CONFIG_TERM_VAL(PERIOD, period, term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ: case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
ADD_CONFIG_TERM_VAL(FREQ, freq, term->val.num); ADD_CONFIG_TERM_VAL(FREQ, freq, term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_TIME: case PARSE_EVENTS__TERM_TYPE_TIME:
ADD_CONFIG_TERM_VAL(TIME, time, term->val.num); ADD_CONFIG_TERM_VAL(TIME, time, term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH: case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
ADD_CONFIG_TERM_STR(CALLGRAPH, term->val.str); ADD_CONFIG_TERM_STR(CALLGRAPH, term->val.str, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
ADD_CONFIG_TERM_STR(BRANCH, term->val.str); ADD_CONFIG_TERM_STR(BRANCH, term->val.str, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_STACKSIZE: case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
ADD_CONFIG_TERM_VAL(STACK_USER, stack_user, ADD_CONFIG_TERM_VAL(STACK_USER, stack_user,
term->val.num); term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_INHERIT: case PARSE_EVENTS__TERM_TYPE_INHERIT:
ADD_CONFIG_TERM_VAL(INHERIT, inherit, ADD_CONFIG_TERM_VAL(INHERIT, inherit,
term->val.num ? 1 : 0); term->val.num ? 1 : 0, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_NOINHERIT: case PARSE_EVENTS__TERM_TYPE_NOINHERIT:
ADD_CONFIG_TERM_VAL(INHERIT, inherit, ADD_CONFIG_TERM_VAL(INHERIT, inherit,
term->val.num ? 0 : 1); term->val.num ? 0 : 1, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_MAX_STACK: case PARSE_EVENTS__TERM_TYPE_MAX_STACK:
ADD_CONFIG_TERM_VAL(MAX_STACK, max_stack, ADD_CONFIG_TERM_VAL(MAX_STACK, max_stack,
term->val.num); term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS: case PARSE_EVENTS__TERM_TYPE_MAX_EVENTS:
ADD_CONFIG_TERM_VAL(MAX_EVENTS, max_events, ADD_CONFIG_TERM_VAL(MAX_EVENTS, max_events,
term->val.num); term->val.num, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_OVERWRITE: case PARSE_EVENTS__TERM_TYPE_OVERWRITE:
ADD_CONFIG_TERM_VAL(OVERWRITE, overwrite, ADD_CONFIG_TERM_VAL(OVERWRITE, overwrite,
term->val.num ? 1 : 0); term->val.num ? 1 : 0, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE: case PARSE_EVENTS__TERM_TYPE_NOOVERWRITE:
ADD_CONFIG_TERM_VAL(OVERWRITE, overwrite, ADD_CONFIG_TERM_VAL(OVERWRITE, overwrite,
term->val.num ? 0 : 1); term->val.num ? 0 : 1, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_DRV_CFG: case PARSE_EVENTS__TERM_TYPE_DRV_CFG:
ADD_CONFIG_TERM_STR(DRV_CFG, term->val.str); ADD_CONFIG_TERM_STR(DRV_CFG, term->val.str, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_PERCORE: case PARSE_EVENTS__TERM_TYPE_PERCORE:
ADD_CONFIG_TERM_VAL(PERCORE, percore, ADD_CONFIG_TERM_VAL(PERCORE, percore,
term->val.num ? true : false); term->val.num ? true : false, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT: case PARSE_EVENTS__TERM_TYPE_AUX_OUTPUT:
ADD_CONFIG_TERM_VAL(AUX_OUTPUT, aux_output, ADD_CONFIG_TERM_VAL(AUX_OUTPUT, aux_output,
term->val.num ? 1 : 0); term->val.num ? 1 : 0, term->weak);
break; break;
case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE: case PARSE_EVENTS__TERM_TYPE_AUX_SAMPLE_SIZE:
ADD_CONFIG_TERM_VAL(AUX_SAMPLE_SIZE, aux_sample_size, ADD_CONFIG_TERM_VAL(AUX_SAMPLE_SIZE, aux_sample_size,
term->val.num); term->val.num, term->weak);
break; break;
default: default:
break; break;
@ -1339,7 +1339,7 @@ static int get_config_chgs(struct perf_pmu *pmu, struct list_head *head_config,
} }
if (bits) if (bits)
ADD_CONFIG_TERM_VAL(CFG_CHG, cfg_chg, bits); ADD_CONFIG_TERM_VAL(CFG_CHG, cfg_chg, bits, false);
#undef ADD_CONFIG_TERM #undef ADD_CONFIG_TERM
return 0; return 0;