Re: [PATCH v8 2/8] plugins/udevng: Add support for PCIe MBIM modems

Andres Salomon <[email protected]> Sat, 13 Dec 2025 03:35:13 -0500
Newsgroups dev.linux.lists.ofono
Message-ID <[email protected]>
On 12/12/25 16:02, Muhammad Asif wrote:
> Parses through the sysfs tree and detects MBIM control nodes, net and
> AT nodes
> ---
>   plugins/udevng.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 110 insertions(+), 1 deletion(-)
> 
> diff --git a/plugins/udevng.c b/plugins/udevng.c
> index 18954d44..97d64466 100644
> --- a/plugins/udevng.c
> +++ b/plugins/udevng.c
> @@ -1182,6 +1182,109 @@ static gboolean setup_quectelqmi(struct modem_info *modem)
>   	return TRUE;
>   }
>   
> +static void setup_mbim_pci(struct device_info *info,
> +				const char **ctl,
> +				const char **net,
> +				const char **atcmd)
> +{
> +	struct udev *udev = udev_new();
> +	struct udev_device *wwan_device, *sub_device;
> +
> +	GDir *dir = NULL, *subdir = NULL;
> +	const gchar *filename = NULL, *subfile = NULL;
> +	gchar *path = NULL;
> +	gchar *wwan_path = NULL, *sub_path = NULL;
> +	const char *sub_subsystem = NULL;
> +	const char *type = NULL;
> +
> +	/* Create a path to the WWAN subsystem with the sysfs path of the
> +	 * modem itself. e.g.: /devices/pci0000:00/0000:00:1c.0/0000:08:00.0
> +	 */
> +	path = g_build_path("/", udev_device_get_syspath(info->udev_device),
> +							"wwan", NULL);
> +
> +	dir = g_dir_open(path, 0, NULL);
> +	if (!dir)
> +		goto cleanup;
> +
> +	while ((filename = g_dir_read_name(dir))) {
> +		/* Build a path to the WWAN interface (e.g. .../wwan0).
> +		 * TODO: We currently assume only one WWAN interface per device.
> +		 * This will most likely break with multi-executor modems, but I have
> +		 * yet to see any of them available on the market.
> +		 */
> +		wwan_path = g_build_path("/", path,
> +					filename, NULL);
> +
> +		/* Check if the WWAN interface even exists. */
> +		if (!g_file_test(wwan_path, G_FILE_TEST_IS_DIR)) {
> +			g_free((void *)wwan_path);
> +			continue;
> +		}
> +
> +		subdir = g_dir_open(wwan_path, 0, NULL);
> +		if (!subdir) {
> +			g_free((void *)wwan_path);
> +			continue;
> +		}
> +
> +		wwan_device = udev_device_new_from_syspath(udev,
> +							wwan_path);
> +
> +		/* Only copy the WWAN interface name once */
> +		if (!*net)
> +			*net = g_strdup(udev_device_get_sysname(wwan_device));
> +
> +		/* The WWAN directory will now have subdirectories, for each
> +		 * associated control node (e.g. wwan0mbim0, wwan0at0). Open
> +		 * each of the subdirectories to figure out their type.

Thanks, this makes much more sense!


> +		 */
> +		while ((subfile = g_dir_read_name(subdir))) {
> +			/* Build a path to the directory for each of the WWAN
> +			 * control nodes that we found earlier. (e.g. /wwan0/wwan0mbim0).
> +			 * Check if the subdirectory belongs to the WWAN subsystem,
> +			 * and if so, check its type (MBIM or AT).
> +			 */
> +			sub_path = g_build_filename("/", wwan_path,
> +							subfile, NULL);
> +
> +			/* We only want the subdirectories for the control nodes. */
> +			if (!g_file_test(sub_path, G_FILE_TEST_IS_DIR)) {
> +				g_free((void *)sub_path);
> +				continue;
> +			}
> +
> +			sub_device = udev_device_new_from_syspath(udev,
> +								sub_path);
> +			sub_subsystem = udev_device_get_subsystem(sub_device);
> +
> +			/* We can only have one MBIM and AT node in each WWAN interface,
> +			 * so set them without checking if they're already set.
> +			 */
> +			if (g_strcmp0(sub_subsystem, "wwan") == 0) {
> +				type = udev_device_get_sysattr_value(sub_device, "type");
> +
> +				/* Detect the type of the WWAN control node */
> +				if (g_strcmp0(type, "MBIM") == 0)
> +					*ctl = g_strdup(udev_device_get_devnode(sub_device));
> +				else if (g_strcmp0(type, "AT") == 0)
> +					*atcmd = g_strdup(udev_device_get_devnode(sub_device));
> +			}
> +
> +			udev_device_unref(sub_device);
> +			g_free((void *)sub_path);
> +		}
> +		g_dir_close(subdir);
> +		udev_device_unref(wwan_device);
> +		g_free((void *)wwan_path);
> +	}
> +
> +cleanup:
> +	g_dir_close(dir);
> +	g_free((void *)path);
> +	udev_unref(udev);
> +}
> +
>   static gboolean setup_mbim(struct modem_info *modem)
>   {
>   	const char *ctl = NULL, *net = NULL, *atcmd = NULL;
> @@ -1206,7 +1309,8 @@ static gboolean setup_mbim(struct modem_info *modem)
>   		else if (g_strcmp0(subsystem, "tty") == 0) {
>   			if (g_strcmp0(info->number, "02") == 0)
>   				atcmd = info->devnode;
> -		}
> +		} else if (g_strcmp0(subsystem, "pci") == 0)
> +			setup_mbim_pci(info, &ctl, &net, &atcmd);
>   	}
>   
>   	if (ctl == NULL || net == NULL)
> @@ -1220,6 +1324,11 @@ static gboolean setup_mbim(struct modem_info *modem)
>   	ofono_modem_set_string(modem->modem, "NetworkInterface", net);
>   	ofono_modem_set_string(modem->modem, "DescriptorFile", descriptors);
>   
> +	/* Free up all of the strings */
> +	g_free((void *)ctl);
> +	g_free((void *)net);
> +	g_free((void *)atcmd);
> +

Sadly, I don't think that'll work. In the case of other types of 
(non-pci) devices, ctl and net will point to memory that we didn't 
allocate. For example, for a usbmisc device, ctl will point to the same 
chunk of memory as info->devnode. If you free it here, later on when 
device_info_free() is called on that device_info, it will try to free 
the same chunk of memory and you'll get a double free.

One possibility is something like the following:

	bool free_strings = false;
[...]
		} else if (g_strcmp0(subsystem, "pci") == 0) {
			free_strings = true;
			setup_mbim_pci(info, &ctl, &net, &atcmd);
		}
[...]
	if (free_strings) {
		g_free(ctl);
		g_free(net);
		g_free(atcmd);
	}

Note that setup_mbim_pci() might not set anything, so you could be 
calling g_free() on NULL pointers, but g_free does check for that (it 
might spit out a warning though, I forget).


>   	return TRUE;
>   }
>