Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf parse-events: Fix big-endian 'overwrite' by writing correct union member

The "Read backward ring buffer" test crashes on big-endian (e.g. s390x)
due to a NULL dereference when the backward mmap path isn't enabled.

Reproducer:
# ./perf test -F 'Read backward ring buffer'
Segmentation fault (core dumped)
# uname -m
s390x
#

Root cause:
get_config_terms() stores into evsel_config_term::val.val (u64) while later
code reads boolean fields such as evsel_config_term::val.overwrite.
On big-endian the 1-byte boolean is left-aligned, so writing
evsel_config_term::val.val = 1 is read back as
evsel_config_term::val.overwrite = 0,
leaving backward mmap disabled and a NULL map being used.

Store values in the union member that matches the term type, e.g.:
/* for OVERWRITE */
new_term->val.overwrite = 1; /* not new_term->val.val = 1 */
to fix this. Improve add_config_term() and add two more parameters for
string and value. Function add_config_term() now creates a complete node
element of type evsel_config_term and handles all evsel_config_term::val
union members.

Impact:
Enables backward mmap on big-endian and prevents the crash.
No change on little-endian.

Output after:
# ./perf test -Fv 44
--- start ---
Using CPUID IBM,9175,705,ME1,3.8,002f
mmap size 1052672B
mmap size 8192B
---- end ----
44: Read backward ring buffer : Ok
#

Fixes: 159ca97cd97ce8cc ("perf parse-events: Refactor get_config_terms() to remove macros")
Reviewed-by: James Clark <james.clark@linaro.org>
Reviewed-by: Jan Polensky <japo@linux.ibm.com>
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Acked-by: Ian Rogers <irogers@google.com>
Cc: James Clark <james.clark@linaro.org>
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Thomas Richter and committed by
Arnaldo Carvalho de Melo
72a8b9c0 8dd1d9a3

+65 -17
+65 -17
tools/perf/util/parse-events.c
··· 1117 1117 1118 1118 static struct evsel_config_term *add_config_term(enum evsel_term_type type, 1119 1119 struct list_head *head_terms, 1120 - bool weak) 1120 + bool weak, char *str, u64 val) 1121 1121 { 1122 1122 struct evsel_config_term *t; 1123 1123 ··· 1128 1128 INIT_LIST_HEAD(&t->list); 1129 1129 t->type = type; 1130 1130 t->weak = weak; 1131 - list_add_tail(&t->list, head_terms); 1132 1131 1132 + switch (type) { 1133 + case EVSEL__CONFIG_TERM_PERIOD: 1134 + case EVSEL__CONFIG_TERM_FREQ: 1135 + case EVSEL__CONFIG_TERM_STACK_USER: 1136 + case EVSEL__CONFIG_TERM_USR_CHG_CONFIG: 1137 + case EVSEL__CONFIG_TERM_USR_CHG_CONFIG1: 1138 + case EVSEL__CONFIG_TERM_USR_CHG_CONFIG2: 1139 + case EVSEL__CONFIG_TERM_USR_CHG_CONFIG3: 1140 + case EVSEL__CONFIG_TERM_USR_CHG_CONFIG4: 1141 + t->val.val = val; 1142 + break; 1143 + case EVSEL__CONFIG_TERM_TIME: 1144 + t->val.time = val; 1145 + break; 1146 + case EVSEL__CONFIG_TERM_INHERIT: 1147 + t->val.inherit = val; 1148 + break; 1149 + case EVSEL__CONFIG_TERM_OVERWRITE: 1150 + t->val.overwrite = val; 1151 + break; 1152 + case EVSEL__CONFIG_TERM_MAX_STACK: 1153 + t->val.max_stack = val; 1154 + break; 1155 + case EVSEL__CONFIG_TERM_MAX_EVENTS: 1156 + t->val.max_events = val; 1157 + break; 1158 + case EVSEL__CONFIG_TERM_PERCORE: 1159 + t->val.percore = val; 1160 + break; 1161 + case EVSEL__CONFIG_TERM_AUX_OUTPUT: 1162 + t->val.aux_output = val; 1163 + break; 1164 + case EVSEL__CONFIG_TERM_AUX_SAMPLE_SIZE: 1165 + t->val.aux_sample_size = val; 1166 + break; 1167 + case EVSEL__CONFIG_TERM_CALLGRAPH: 1168 + case EVSEL__CONFIG_TERM_BRANCH: 1169 + case EVSEL__CONFIG_TERM_DRV_CFG: 1170 + case EVSEL__CONFIG_TERM_RATIO_TO_PREV: 1171 + case EVSEL__CONFIG_TERM_AUX_ACTION: 1172 + if (str) { 1173 + t->val.str = strdup(str); 1174 + if (!t->val.str) { 1175 + zfree(&t); 1176 + return NULL; 1177 + } 1178 + t->free_str = true; 1179 + } 1180 + break; 1181 + default: 1182 + t->val.val = val; 1183 + break; 1184 + } 1185 + 1186 + list_add_tail(&t->list, head_terms); 1133 1187 return t; 1134 1188 } 1135 1189 ··· 1196 1142 struct evsel_config_term *new_term; 1197 1143 enum evsel_term_type new_type; 1198 1144 bool str_type = false; 1199 - u64 val; 1145 + u64 val = 0; 1200 1146 1201 1147 switch (term->type_term) { 1202 1148 case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: ··· 1288 1234 continue; 1289 1235 } 1290 1236 1291 - new_term = add_config_term(new_type, head_terms, term->weak); 1237 + /* 1238 + * Note: Members evsel_config_term::val and 1239 + * parse_events_term::val are unions and endianness needs 1240 + * to be taken into account when changing such union members. 1241 + */ 1242 + new_term = add_config_term(new_type, head_terms, term->weak, 1243 + str_type ? term->val.str : NULL, val); 1292 1244 if (!new_term) 1293 1245 return -ENOMEM; 1294 - 1295 - if (str_type) { 1296 - new_term->val.str = strdup(term->val.str); 1297 - if (!new_term->val.str) { 1298 - zfree(&new_term); 1299 - return -ENOMEM; 1300 - } 1301 - new_term->free_str = true; 1302 - } else { 1303 - new_term->val.val = val; 1304 - } 1305 1246 } 1306 1247 return 0; 1307 1248 } ··· 1326 1277 if (bits) { 1327 1278 struct evsel_config_term *new_term; 1328 1279 1329 - new_term = add_config_term(new_term_type, head_terms, false); 1280 + new_term = add_config_term(new_term_type, head_terms, false, NULL, bits); 1330 1281 if (!new_term) 1331 1282 return -ENOMEM; 1332 - new_term->val.cfg_chg = bits; 1333 1283 } 1334 1284 1335 1285 return 0;