[PATCH] Fix comparison of services of same preferred type

Johannes Emerich <[email protected]> Fri, 1 Aug 2025 13:02:39 +0200
Newsgroups dev.linux.lists.connman
Message-ID <[email protected]>
The service_compare_preferred() function is called twice in
service_compare(), once with a guard to only use it on services with
differing types, but once without any such guard. In the latter case,
when comparing two services of the same preferred technology, it always
returns -1 because it checks service_a first and exits immediately, when
one would normally expect that comparison to result in a tie (return
value 0).

This causes the parent service_compare() function to terminate its
comparison early instead of applying appropriate tie-breakers like
online status, Strength or Name. The result is an unstable sort order
that depends on the initial order of services.

This patch adds a condition to service_compare_preferred() so that
the preference logic is only applied to services of different types,
without relying on calling contexts to make such checks.

I ran into this issue when reading sources to understand how ConnMan
would sort two connected Ethernet services, when one is 'online' and one
is 'ready' and 'ethernet' is in the preferred technologies list. It
seems that their online state would in fact not matter at all, instead
the service that happened to appear first in the list would win.

The issue may have been introduced in
31d9ce64a06743cc69b92c47a36e5ace0f9cb030, when
service_compare_preferred() was extracted from its original context in
which the comparison was only applied to services of unequal types.

Signed-off-by: Johannes Emerich <[email protected]>
---
 src/service.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/service.c b/src/service.c
index f73f42eb..d5869b14 100644
--- a/src/service.c
+++ b/src/service.c
@@ -8299,9 +8299,9 @@ static gint service_compare_vpn(const struct connman_service *a,
  *                         to compare by its technology type with the
  *                         @a PreferredTechnologies priority list.
  *
- *  @retval   0  If the @a PreferredTechnologies configuration is empty
- *               or if neither service type matches a technology type
- *               in the @a PreferredTechnologies list.
+ *  @retval   0  If neither service type appears in the
+ *               @a PreferredTechnologies list (including when
+ *               it is empty), or if the two service types are equal.
  *  @retval  -1  If @a service_a type matches a technology type
  *               in the @a PreferredTechnologies list and should sort
  *               @b before @a service_b.
@@ -8317,7 +8317,7 @@ static gint service_compare_preferred(const struct connman_service *service_a,
 	int i;
 
 	tech_array = connman_setting_get_uint_list("PreferredTechnologies");
-	if (tech_array) {
+	if (tech_array && service_a->type != service_b->type) {
 		for (i = 0; tech_array[i]; i++) {
 			if (tech_array[i] == service_a->type)
 				return -1;
-- 
2.47.2