Rename is_need() to filter_record() and make the filter path return explicit error, skip, and match results. This lets callers distinguish allocation failures from records that simply do not match active filters. Reviewed-by: Vishal Moola Signed-off-by: Yichong Chen --- tools/mm/page_owner_sort.c | 40 +++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c index e6954909401c..3c754826cec5 100644 --- a/tools/mm/page_owner_sort.c +++ b/tools/mm/page_owner_sort.c @@ -43,6 +43,13 @@ enum FILTER_BIT { FILTER_TGID = 1<<2, FILTER_COMM = 1<<3 }; + +enum FILTER_RESULT { + FILTER_ERROR, + FILTER_SKIP, + FILTER_MATCH +}; + enum CULL_BIT { CULL_PID = 1<<1, CULL_TGID = 1<<2, @@ -372,6 +379,9 @@ static char *get_comm(char *buf) { char *comm_str = malloc(TASK_COMM_LEN); + if (!comm_str) + return NULL; + memset(comm_str, 0, TASK_COMM_LEN); search_pattern(&comm_pattern, comm_str, buf); @@ -450,32 +460,44 @@ static bool match_str_list(const char *str, char **list, int list_size) return false; } -static bool is_need(char *buf) +static enum FILTER_RESULT filter_record(char *buf) { + char *comm; + if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size)) - return false; + return FILTER_SKIP; if ((filter & FILTER_TGID) && !match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size)) - return false; + return FILTER_SKIP; + if (!(filter & FILTER_COMM)) + return FILTER_MATCH; - char *comm = get_comm(buf); + comm = get_comm(buf); + if (!comm) + return FILTER_ERROR; - if ((filter & FILTER_COMM) && - !match_str_list(comm, fc.comms, fc.comms_size)) { + if (!match_str_list(comm, fc.comms, fc.comms_size)) { free(comm); - return false; + return FILTER_SKIP; } free(comm); - return true; + return FILTER_MATCH; } static bool add_list(char *buf, int len, char *ext_buf) { + enum FILTER_RESULT filter_result; + if (list_size == max_size) { fprintf(stderr, "max_size too small??\n"); return false; } - if (!is_need(buf)) + filter_result = filter_record(buf); + if (filter_result == FILTER_ERROR) { + fprintf(stderr, "Out of memory\n"); + return false; + } + if (filter_result == FILTER_SKIP) return true; list[list_size].pid = get_pid(buf); list[list_size].tgid = get_tgid(buf); -- 2.51.0 add_list() allocates comm and txt for each page owner record, but the cleanup path only frees the outer list array. This leaks both buffers for every retained record. Free partial allocations in add_list(), discarded records during culling, and retained records on exit. Reviewed-by: Vishal Moola Signed-off-by: Yichong Chen --- tools/mm/page_owner_sort.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c index 3c754826cec5..4c9be28abe3b 100644 --- a/tools/mm/page_owner_sort.c +++ b/tools/mm/page_owner_sort.c @@ -396,6 +396,12 @@ static char *get_comm(char *buf) return comm_str; } +static void free_block_list(struct block_list *block) +{ + free(block->comm); + free(block->txt); +} + static int get_arg_type(const char *arg) { if (!strcmp(arg, "pid") || !strcmp(arg, "p")) @@ -502,9 +508,14 @@ static bool add_list(char *buf, int len, char *ext_buf) list[list_size].pid = get_pid(buf); list[list_size].tgid = get_tgid(buf); list[list_size].comm = get_comm(buf); - list[list_size].txt = malloc(len+1); + if (!list[list_size].comm) { + fprintf(stderr, "Out of memory\n"); + return false; + } + list[list_size].txt = malloc(len + 1); if (!list[list_size].txt) { fprintf(stderr, "Out of memory\n"); + free(list[list_size].comm); return false; } memcpy(list[list_size].txt, buf, len); @@ -863,8 +874,10 @@ int main(int argc, char **argv) } else { list[count-1].num += list[i].num; list[count-1].page_num += list[i].page_num; + free_block_list(&list[i]); } } + list_size = count; qsort(list, count, sizeof(list[0]), compare_sort_condition); @@ -898,8 +911,11 @@ int main(int argc, char **argv) free(ext_buf); if (buf) free(buf); - if (list) + if (list) { + for (i = 0; i < list_size; i++) + free_block_list(&list[i]); free(list); + } out_ts: regfree(&ts_nsec_pattern); out_comm: -- 2.51.0 search_pattern() copies a regex capture into caller-provided buffers without knowing their sizes. Several callers pass fixed-size buffers, including FIELD_BUFF and TASK_COMM_LEN. Pass the destination size to search_pattern(), reject captures that do not fit before copying them, and terminate the output string inside search_pattern(). Signed-off-by: Yichong Chen --- tools/mm/page_owner_sort.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/tools/mm/page_owner_sort.c b/tools/mm/page_owner_sort.c index 4c9be28abe3b..35d3d254941c 100644 --- a/tools/mm/page_owner_sort.c +++ b/tools/mm/page_owner_sort.c @@ -237,7 +237,8 @@ static int remove_pattern(regex_t *pattern, char *buf, int len) return len - (pmatch[1].rm_eo - pmatch[1].rm_so); } -static int search_pattern(regex_t *pattern, char *pattern_str, char *buf) +static int search_pattern(regex_t *pattern, char *pattern_str, + size_t pattern_str_size, char *buf) { int err, val_len; regmatch_t pmatch[2]; @@ -249,6 +250,12 @@ static int search_pattern(regex_t *pattern, char *pattern_str, char *buf) return -1; } val_len = pmatch[1].rm_eo - pmatch[1].rm_so; + if ((size_t)val_len >= pattern_str_size) { + if (debug_on) + fprintf(stderr, "pattern too long in %s\n", buf); + return -1; + } memcpy(pattern_str, buf + pmatch[1].rm_so, val_len); + pattern_str[val_len] = '\0'; @@ -307,7 +314,8 @@ static int get_page_num(char *buf) char order_str[FIELD_BUFF] = {0}; char *endptr; - search_pattern(&order_pattern, order_str, buf); + if (search_pattern(&order_pattern, order_str, sizeof(order_str), buf) < 0) + return 0; errno = 0; order_val = strtol(order_str, &endptr, 10); if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') { @@ -325,7 +333,8 @@ static pid_t get_pid(char *buf) char pid_str[FIELD_BUFF] = {0}; char *endptr; - search_pattern(&pid_pattern, pid_str, buf); + if (search_pattern(&pid_pattern, pid_str, sizeof(pid_str), buf) < 0) + return -1; errno = 0; pid = strtol(pid_str, &endptr, 10); if (errno != 0 || endptr == pid_str || *endptr != '\0') { @@ -344,7 +353,8 @@ static pid_t get_tgid(char *buf) char tgid_str[FIELD_BUFF] = {0}; char *endptr; - search_pattern(&tgid_pattern, tgid_str, buf); + if (search_pattern(&tgid_pattern, tgid_str, sizeof(tgid_str), buf) < 0) + return -1; errno = 0; tgid = strtol(tgid_str, &endptr, 10); if (errno != 0 || endptr == tgid_str || *endptr != '\0') { @@ -363,7 +373,9 @@ static __u64 get_ts_nsec(char *buf) char ts_nsec_str[FIELD_BUFF] = {0}; char *endptr; - search_pattern(&ts_nsec_pattern, ts_nsec_str, buf); + if (search_pattern(&ts_nsec_pattern, ts_nsec_str, + sizeof(ts_nsec_str), buf) < 0) + return -1; errno = 0; ts_nsec = strtoull(ts_nsec_str, &endptr, 10); if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') { @@ -384,7 +396,10 @@ static char *get_comm(char *buf) memset(comm_str, 0, TASK_COMM_LEN); - search_pattern(&comm_pattern, comm_str, buf); + if (search_pattern(&comm_pattern, comm_str, TASK_COMM_LEN, buf) < 0) { + free(comm_str); + return NULL; + } errno = 0; if (errno != 0) { if (debug_on) -- 2.51.0