net: ti: icss-iep: Fix possible NULL pointer dereference for perout request
authorMeghana Malladi <m-malladi@ti.com>
Tue, 15 Apr 2025 09:05:43 +0000 (14:35 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 25 Apr 2025 08:45:44 +0000 (10:45 +0200)
[ Upstream commit 7349c9e9979333abfce42da5f9025598083b59c9 ]

The ICSS IEP driver tracks perout and pps enable state with flags.
Currently when disabling pps and perout signals during icss_iep_exit(),
results in NULL pointer dereference for perout.

To fix the null pointer dereference issue, the icss_iep_perout_enable_hw
function can be modified to directly clear the IEP CMP registers when
disabling PPS or PEROUT, without referencing the ptp_perout_request
structure, as its contents are irrelevant in this case.

Fixes: 9b115361248d ("net: ti: icssg-prueth: Fix clearing of IEP_CMP_CFG registers during iep_init")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/all/7b1c7c36-363a-4085-b26c-4f210bee1df6@stanley.mountain/
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
Link: https://patch.msgid.link/20250415090543.717991-4-m-malladi@ti.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/net/ethernet/ti/icssg/icss_iep.c

index 6006276ecce6f0b98965672d87a2a7eb9ec477b0..f3315c65151561a60d860fbf5e4db786d894fcf3 100644 (file)
@@ -482,6 +482,22 @@ static int icss_iep_perout_enable_hw(struct icss_iep *iep,
        int ret;
        u64 cmp;
 
+       if (!on) {
+               /* Disable CMP 1 */
+               regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
+                                  IEP_CMP_CFG_CMP_EN(1), 0);
+
+               /* clear CMP regs */
+               regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);
+               if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
+                       regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);
+
+               /* Disable sync */
+               regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0);
+
+               return 0;
+       }
+
        /* Calculate width of the signal for PPS/PEROUT handling */
        ts.tv_sec = req->on.sec;
        ts.tv_nsec = req->on.nsec;
@@ -500,64 +516,39 @@ static int icss_iep_perout_enable_hw(struct icss_iep *iep,
                if (ret)
                        return ret;
 
-               if (on) {
-                       /* Configure CMP */
-                       regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp));
-                       if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
-                               regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp));
-                       /* Configure SYNC, based on req on width */
-                       regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG,
-                                    div_u64(ns_width, iep->def_inc));
-                       regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);
-                       regmap_write(iep->map, ICSS_IEP_SYNC_START_REG,
-                                    div_u64(ns_start, iep->def_inc));
-                       regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */
-                       /* Enable CMP 1 */
-                       regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
-                                          IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));
-               } else {
-                       /* Disable CMP 1 */
-                       regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
-                                          IEP_CMP_CFG_CMP_EN(1), 0);
-
-                       /* clear regs */
-                       regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);
-                       if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
-                               regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);
-               }
+               /* Configure CMP */
+               regmap_write(iep->map, ICSS_IEP_CMP1_REG0, lower_32_bits(cmp));
+               if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
+                       regmap_write(iep->map, ICSS_IEP_CMP1_REG1, upper_32_bits(cmp));
+               /* Configure SYNC, based on req on width */
+               regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG,
+                            div_u64(ns_width, iep->def_inc));
+               regmap_write(iep->map, ICSS_IEP_SYNC0_PERIOD_REG, 0);
+               regmap_write(iep->map, ICSS_IEP_SYNC_START_REG,
+                            div_u64(ns_start, iep->def_inc));
+               regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0); /* one-shot mode */
+               /* Enable CMP 1 */
+               regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
+                                  IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));
        } else {
-               if (on) {
-                       u64 start_ns;
-
-                       iep->period = ((u64)req->period.sec * NSEC_PER_SEC) +
-                                     req->period.nsec;
-                       start_ns = ((u64)req->period.sec * NSEC_PER_SEC)
-                                  + req->period.nsec;
-                       icss_iep_update_to_next_boundary(iep, start_ns);
-
-                       regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG,
-                                    div_u64(ns_width, iep->def_inc));
-                       regmap_write(iep->map, ICSS_IEP_SYNC_START_REG,
-                                    div_u64(ns_start, iep->def_inc));
-                       /* Enable Sync in single shot mode  */
-                       regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG,
-                                    IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN);
-                       /* Enable CMP 1 */
-                       regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
-                                          IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));
-               } else {
-                       /* Disable CMP 1 */
-                       regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
-                                          IEP_CMP_CFG_CMP_EN(1), 0);
-
-                       /* clear CMP regs */
-                       regmap_write(iep->map, ICSS_IEP_CMP1_REG0, 0);
-                       if (iep->plat_data->flags & ICSS_IEP_64BIT_COUNTER_SUPPORT)
-                               regmap_write(iep->map, ICSS_IEP_CMP1_REG1, 0);
-
-                       /* Disable sync */
-                       regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG, 0);
-               }
+               u64 start_ns;
+
+               iep->period = ((u64)req->period.sec * NSEC_PER_SEC) +
+                               req->period.nsec;
+               start_ns = ((u64)req->period.sec * NSEC_PER_SEC)
+                               + req->period.nsec;
+               icss_iep_update_to_next_boundary(iep, start_ns);
+
+               regmap_write(iep->map, ICSS_IEP_SYNC_PWIDTH_REG,
+                            div_u64(ns_width, iep->def_inc));
+               regmap_write(iep->map, ICSS_IEP_SYNC_START_REG,
+                            div_u64(ns_start, iep->def_inc));
+               /* Enable Sync in single shot mode  */
+               regmap_write(iep->map, ICSS_IEP_SYNC_CTRL_REG,
+                            IEP_SYNC_CTRL_SYNC_N_EN(0) | IEP_SYNC_CTRL_SYNC_EN);
+               /* Enable CMP 1 */
+               regmap_update_bits(iep->map, ICSS_IEP_CMP_CFG_REG,
+                                  IEP_CMP_CFG_CMP_EN(1), IEP_CMP_CFG_CMP_EN(1));
        }
 
        return 0;
@@ -568,11 +559,21 @@ static int icss_iep_perout_enable(struct icss_iep *iep,
 {
        int ret = 0;
 
+       if (!on)
+               goto disable;
+
        /* Reject requests with unsupported flags */
        if (req->flags & ~(PTP_PEROUT_DUTY_CYCLE |
                          PTP_PEROUT_PHASE))
                return -EOPNOTSUPP;
 
+       /* Set default "on" time (1ms) for the signal if not passed by the app */
+       if (!(req->flags & PTP_PEROUT_DUTY_CYCLE)) {
+               req->on.sec = 0;
+               req->on.nsec = NSEC_PER_MSEC;
+       }
+
+disable:
        mutex_lock(&iep->ptp_clk_mutex);
 
        if (iep->pps_enabled) {
@@ -583,12 +584,6 @@ static int icss_iep_perout_enable(struct icss_iep *iep,
        if (iep->perout_enabled == !!on)
                goto exit;
 
-       /* Set default "on" time (1ms) for the signal if not passed by the app */
-       if (!(req->flags & PTP_PEROUT_DUTY_CYCLE)) {
-               req->on.sec = 0;
-               req->on.nsec = NSEC_PER_MSEC;
-       }
-
        ret = icss_iep_perout_enable_hw(iep, req, on);
        if (!ret)
                iep->perout_enabled = !!on;