lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <91690d5c-7f46-4f6d-8140-c6013d84a0f1@arm.com>
Date: Fri, 11 Oct 2024 10:34:38 +0100
From: Leo Yan <leo.yan@....com>
To: Adrian Hunter <adrian.hunter@...el.com>,
 Arnaldo Carvalho de Melo <acme@...nel.org>,
 Namhyung Kim <namhyung@...nel.org>, Mark Rutland <mark.rutland@....com>,
 Alexander Shishkin <alexander.shishkin@...ux.intel.com>,
 Jiri Olsa <jolsa@...nel.org>, Ian Rogers <irogers@...gle.com>,
 "Liang, Kan" <kan.liang@...ux.intel.com>,
 James Clark <james.clark@...aro.org>, linux-perf-users@...r.kernel.org,
 linux-kernel@...r.kernel.org
Subject: Re: [PATCH v1 1/5] libperf cpumap: Correct reference count for
 perf_cpu_map__merge()

Hi Adrian,

On 10/10/24 18:41, Adrian Hunter wrote:>
> On 25/09/24 22:53, Leo Yan wrote:
>> The perf_cpu_map__merge() function has two arguments, 'orig' and 'other',
>> and it returns results for three different cases:
>>
>>    Case 1: 'other' is a subset of 'orig'.
>>    Case 2: 'orig' is a subset of 'other'.
>>    Case 3: 'orig' and 'other' are not subsets of each other.
>>
>> The result combinations are:
>>
>>    +--------+-------------+-----------+-----------+
>>    | Cases  | Result      | orig      | other     |
>>    +--------+-------------+-----------+-----------+
>>    | Case1  | orig        | No change | No change |
>>    +--------+-------------+-----------+-----------+
>>    | Case2  | other       | No change | refcnt++  |
>>    +--------+-------------+-----------+-----------+
>>    | Case3  | New CPU map | refcnt--  | No change |
>>    +--------+-------------+-----------+-----------+
>>
>> Both Case 1 and Case 3 have a risk of releasing maps unexpectedly. This
>> is because the reference counter operations are not consistent crossing
>> different cases and leads to difficulty for callers handling them.
>>
>> For Case 1, because 'other' is a subset of 'orig', 'orig' is returned as
>> the merging result, but its refcnt is not incremented. This can lead to
>> the map being released repeatedly:
>>
>>    struct perf_cpu_map *a = perf_cpu_map__new("1,2");
>>    struct perf_cpu_map *b = perf_cpu_map__new("2");
>>
>>    /* 'm' and 'a' point to the same CPU map */
>>    struct perf_cpu_map *m = perf_cpu_map__merge(a, b);
>>
>>    ...
>>
>>    perf_cpu_map__put(m); -> Release the map
>>    perf_cpu_map__put(b);
>>    perf_cpu_map__put(a); -> Release the same merged again
>>
>> For Case 3, it is possible that the CPU map pointed to by 'orig' can be
>> released twice: within the function and outside of it.
>>
>>    struct perf_cpu_map *a = perf_cpu_map__new("1,2");
>>    struct perf_cpu_map *b = perf_cpu_map__new("3");
>>
>>    struct perf_cpu_map *m = perf_cpu_map__merge(a, b);
>>                               `> 'm' is new allocated map. But 'a' has
>>                                been released in the function.
>>    ...
>>
>>    perf_cpu_map__put(m);
>>    perf_cpu_map__put(b);
>>    perf_cpu_map__put(a); -> Release the 'a' map again
>>
>> This commit increases the reference counter if a passed map is returned.
>> For the case of a newly allocated map, it does not change the reference
>> counter for passed maps.
> 
> The 2 non-test uses of perf_cpu_map__merge both do:
> 
>          a = perf_cpu_map__merge(a, b);
> 
> so another way to make the API less misleading would be
> to introduce:
> 
>          err = perf_cpu_map__merge_in(&a, b);
> 
> where:
> 
> int perf_cpu_map__merge_in(struct perf_cpu_map **orig, struct perf_cpu_map *other)
> {
>          struct perf_cpu_map *result = perf_cpu_map__merge(*orig, other);
> 
>          if (!result)
>                  return -ENOMEM;
> 
>          *orig = result;
>          return 0;
> }
> 
> without any changes to perf_cpu_map__merge().

Just wandering why we cannot do the same thing for the perf_cpu_map__merge() 
function?

   int perf_cpu_map__merge_in(struct perf_cpu_map **orig,
                              struct perf_cpu_map *other)

This can allow us to avoid any confusion in the first place. And we don't need 
to maintain two functions for the same thing.

Thanks,
Leo


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ