In secondary processes, directly accessing 'dev->data->dev_private' can
cause a segmentation fault if the primary process has exited or if the
shared memory is no longer accessible.

The secondary process not only crashes on device close but also segfaults
when executing commands like "show device info all" after the primary has
exited.

This patch adds a mechanism in testpmd to monitor the primary process from the
secondary using `rte_eal_primary_proc_alive()`, and introduces safety
checks before dereferencing 'dev_private'. The `rte_mem_virt2phy()` call
is also guarded to avoid access to invalid memory regions, with
`unlikely()` hints to minimize impact in the fast path.

Fixes: bdad90d12ec8 ("ethdev: change device info get callback to return int")
Cc: sta...@dpdk.org

Signed-off-by: Khadem Ullah <14pwcse1...@uetpeshawar.edu.pk>
---
 app/test-pmd/testpmd.c  | 56 ++++++++++++++++++++++++++++++++++++++---
 lib/ethdev/rte_ethdev.c | 15 ++++++++++-
 2 files changed, 66 insertions(+), 5 deletions(-)

diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index bb88555328..25e55de281 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -101,13 +101,17 @@
 uint16_t verbose_level = 0; /**< Silent by default. */
 int testpmd_logtype; /**< Log type for testpmd logs */
 
+static volatile uint8_t quit_signal;
+
+/* Maximum delay for exiting after primary process. */
+#define MONITOR_INTERVAL (500 * 1000)
+
 /* use main core for command line ? */
 uint8_t interactive = 0;
 uint8_t auto_start = 0;
 uint8_t tx_first;
 char cmdline_filename[PATH_MAX] = {0};
 bool echo_cmdline_file;
-
 /*
  * NUMA support configuration.
  * When set, the NUMA support attempts to dispatch the allocation of the
@@ -2281,7 +2285,7 @@ run_pkt_fwd_on_lcore(struct fwd_lcore *fc, packet_fwd_t 
pkt_fwd)
                        fc->total_cycles += tsc - prev_tsc;
                        prev_tsc = tsc;
                }
-       } while (! fc->stopped);
+       } while (!fc->stopped && !quit_signal);
 }
 
 static int
@@ -4329,9 +4333,51 @@ static void
 signal_handler(int signum __rte_unused)
 {
        f_quit = 1;
+       quit_signal = 1;
        prompt_exit();
 }
 
+
+/* Alarm signal handler, used to check that primary process */
+static void
+monitor_primary(void *arg __rte_unused)
+{
+       if (rte_atomic_load_explicit(&quit_signal, rte_memory_order_relaxed))
+               return;
+
+       if (rte_eal_primary_proc_alive(NULL)) {
+               rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
+       } else {
+               fprintf(stderr,
+                       "Primary process is no longer active, exiting...\n");
+               quit_signal = 1;
+               f_quit = 1;
+               prompt_exit();
+       }
+}
+
+/* Setup handler to check when primary exits. */
+static void
+enable_primary_monitor(void)
+{
+       int ret;
+
+       /* Once primary exits, so will pdump. */
+       ret = rte_eal_alarm_set(MONITOR_INTERVAL, monitor_primary, NULL);
+       if (ret < 0)
+               fprintf(stderr, "Fail to enable monitor:%d\n", ret);
+}
+
+static void
+disable_primary_monitor(void)
+{
+       int ret;
+
+       ret = rte_eal_alarm_cancel(monitor_primary, NULL);
+       if (ret < 0)
+               fprintf(stderr, "Fail to disable monitor:%d\n", ret);
+}
+
 int
 main(int argc, char** argv)
 {
@@ -4362,7 +4408,8 @@ main(int argc, char** argv)
        if (diag < 0)
                rte_exit(EXIT_FAILURE, "Cannot init EAL: %s\n",
                         rte_strerror(rte_errno));
-
+       if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+               enable_primary_monitor();
        /* allocate port structures, and init them */
        init_port();
 
@@ -4571,7 +4618,8 @@ main(int argc, char** argv)
        if (ret != 0)
                rte_exit(EXIT_FAILURE, "Cannot unregister for ethdev events");
 
-
+       if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+               disable_primary_monitor();
        ret = rte_eal_cleanup();
        if (ret != 0)
                rte_exit(EXIT_FAILURE,
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index dd7c00bc94..343e156a4f 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -4079,6 +4079,13 @@ rte_eth_dev_info_get(uint16_t port_id, struct 
rte_eth_dev_info *dev_info)
 
        if (dev->dev_ops->dev_infos_get == NULL)
                return -ENOTSUP;
+       if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
+               unlikely(rte_mem_virt2phy(dev->data->dev_private) == 
RTE_BAD_PHYS_ADDR)) {
+                       RTE_ETHDEV_LOG_LINE(ERR,
+                       "Secondary: dev_private not accessible (primary 
exited?)");
+                       rte_errno = ENODEV;
+                       return -rte_errno;
+       }
        diag = dev->dev_ops->dev_infos_get(dev, dev_info);
        if (diag != 0) {
                /* Cleanup already filled in device information */
@@ -4307,7 +4314,13 @@ rte_eth_macaddr_get(uint16_t port_id, struct 
rte_ether_addr *mac_addr)
                        port_id);
                return -EINVAL;
        }
-
+       if (rte_eal_process_type() == RTE_PROC_SECONDARY &&
+               (dev->data->mac_addrs == NULL)) {
+                       RTE_ETHDEV_LOG_LINE(ERR,
+                       "Secondary: dev_private not accessible (primary 
exited?)");
+                       rte_errno = ENODEV;
+                       return -rte_errno;
+       }
        rte_ether_addr_copy(&dev->data->mac_addrs[0], mac_addr);
 
        rte_eth_trace_macaddr_get(port_id, mac_addr);
-- 
2.43.0

Reply via email to