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:
parent
a7ffd416d8
commit
05e54e2386
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user