ltq-ptm: Fix unprivileged local user memory read and write
authorHauke Mehrtens <[email protected]>
Mon, 13 Oct 2025 22:32:26 +0000 (00:32 +0200)
committerHauke Mehrtens <[email protected]>
Fri, 17 Oct 2025 22:47:29 +0000 (00:47 +0200)
Use the copy_from_user() and copy_to_user() functions for accessing
memory provided by the user in the ptm netdev iotls.
In addition also check for root permission before executing ioctl.

Suggested-by: Stanislav Fort from Aisle Research
Reported-by: Stanislav Fort from Aisle Research
Signed-off-by: Hauke Mehrtens <[email protected]>
package/kernel/lantiq/ltq-ptm/patches/100-fix-compilation-warning-debugfs.patch
package/kernel/lantiq/ltq-ptm/patches/101-fix-more-compilation-warning-debugfs.patch
package/kernel/lantiq/ltq-ptm/patches/102-fix-missing-prototypes-warning.patch
package/kernel/lantiq/ltq-ptm/patches/103-add-missing-header-mod_devicetable.h.patch
package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_adsl.c
package/kernel/lantiq/ltq-ptm/src/ifxmips_ptm_vdsl.c

index 7317d511631b0f7064cd7c9f149d22f19c93cf8f..f6c0921b6ef92c4c1cf1cb02c7c80808e64569bb 100644 (file)
@@ -1,6 +1,6 @@
 --- a/ifxmips_ptm_adsl.c
 +++ b/ifxmips_ptm_adsl.c
-@@ -175,9 +175,11 @@ static INLINE void mailbox_signal(unsign
+@@ -178,9 +178,11 @@ static INLINE void mailbox_signal(unsign
   */
  static INLINE void proc_file_create(void);
  static INLINE void proc_file_delete(void);
@@ -12,7 +12,7 @@
  #if defined(ENABLE_FW_PROC) && ENABLE_FW_PROC
    static int proc_read_genconf(char *, char **, off_t, int, int *, void *);
  #endif
-@@ -896,6 +898,7 @@ static INLINE void proc_file_delete(void
+@@ -936,6 +938,7 @@ static INLINE void proc_file_delete(void
      remove_proc_entry("driver/ifx_ptm", NULL);
  }
  
@@ -20,7 +20,7 @@
  static int proc_read_version(char *buf, char **start, off_t offset, int count, int *eof, void *data)
  {
      int len = 0;
-@@ -970,8 +973,9 @@ static int proc_write_wanmib(struct file
+@@ -1010,8 +1013,9 @@ static int proc_write_wanmib(struct file
  
      return count;
  }
index 538d569cbe0db2e4177fe9052ca311edb64265db..76b8a776037ecb3af5ffebacadfc794b512a448a 100644 (file)
@@ -1,6 +1,6 @@
 --- a/ifxmips_ptm_adsl.c
 +++ b/ifxmips_ptm_adsl.c
-@@ -180,7 +180,7 @@ static int proc_read_version(char *, cha
+@@ -183,7 +183,7 @@ static int proc_read_version(char *, cha
  static int proc_read_wanmib(char *, char **, off_t, int, int *, void *);
  static int proc_write_wanmib(struct file *, const char *, unsigned long, void *);
  #endif
@@ -9,7 +9,7 @@
    static int proc_read_genconf(char *, char **, off_t, int, int *, void *);
  #endif
  #if defined(ENABLE_DBG_PROC) && ENABLE_DBG_PROC
-@@ -191,8 +191,8 @@ static int proc_write_wanmib(struct file
+@@ -194,8 +194,8 @@ static int proc_write_wanmib(struct file
  /*
   *  Proc Help Functions
   */
@@ -19,7 +19,7 @@
    static INLINE int strincmp(const char *, const char *, int);
  #endif
  static INLINE int ifx_ptm_version(char *);
-@@ -1166,8 +1166,6 @@ static int proc_write_dbg(struct file *f
+@@ -1206,8 +1206,6 @@ static int proc_write_dbg(struct file *f
      return count;
  }
  
@@ -28,7 +28,7 @@
  static INLINE int stricmp(const char *p1, const char *p2)
  {
      int c1, c2;
-@@ -1185,7 +1183,6 @@ static INLINE int stricmp(const char *p1
+@@ -1225,7 +1223,6 @@ static INLINE int stricmp(const char *p1
      return *p1 - *p2;
  }
  
index faa5784fcada05c7b744c33980dedeb3e1cd9ccc..025542570d7c8c48083932fcbb1abf849734234d 100644 (file)
@@ -15,7 +15,7 @@ Signed-off-by: Shiji Yang <[email protected]>
 
 --- a/ifxmips_ptm_vdsl.c
 +++ b/ifxmips_ptm_vdsl.c
-@@ -61,7 +61,7 @@ static void *g_xdata_addr = NULL;
+@@ -62,7 +62,7 @@ static void *g_xdata_addr = NULL;
  
  #define ENABLE_TMP_DBG                          0
  
index 530f62dda4d1f07d78bf95590afa8e6e965e4958..811ecf920a80e05a14ee721f28d1250aed8870e0 100644 (file)
@@ -22,5 +22,5 @@ Signed-off-by: Shiji Yang <[email protected]>
  #include <linux/platform_device.h>
 +#include <linux/mod_devicetable.h>
  #include <linux/of_device.h>
+ #include <linux/of_net.h>
  
- #include "ifxmips_ptm_vdsl.h"
index 944c941d8b60f9196370edf414e1cf31c8b92cf8..48bc7b04a9d1a04fcc11fb8a7e7aae38f9f6c12f 100644 (file)
@@ -47,6 +47,8 @@
 #include <linux/platform_device.h>
 #include <linux/of_device.h>
 #include <linux/of_net.h>
+#include <linux/uaccess.h>
+#include <linux/capability.h>
 #include <asm/io.h>
 
 /*
@@ -472,56 +474,85 @@ static int ptm_ioctl(struct net_device *dev, struct ifreq *ifr, void __user *dat
 {
     int ndev;
 
+    if (!capable(CAP_NET_ADMIN))
+        return -EPERM;
+
     for ( ndev = 0; ndev < ARRAY_SIZE(g_net_dev) && g_net_dev[ndev] != dev; ndev++ );
     ASSERT(ndev >= 0 && ndev < ARRAY_SIZE(g_net_dev), "ndev = %d (wrong value)", ndev);
 
     switch ( cmd )
     {
     case IFX_PTM_MIB_CW_GET:
-        ((PTM_CW_IF_ENTRY_T *)data)->ifRxNoIdleCodewords   = WAN_MIB_TABLE[ndev].wrx_nonidle_cw;
-        ((PTM_CW_IF_ENTRY_T *)data)->ifRxIdleCodewords     = WAN_MIB_TABLE[ndev].wrx_idle_cw;
-        ((PTM_CW_IF_ENTRY_T *)data)->ifRxCodingViolation   = WAN_MIB_TABLE[ndev].wrx_err_cw;
-        ((PTM_CW_IF_ENTRY_T *)data)->ifTxNoIdleCodewords   = 0;
-        ((PTM_CW_IF_ENTRY_T *)data)->ifTxIdleCodewords     = 0;
-        break;
-    case IFX_PTM_MIB_FRAME_GET:
-        ((PTM_FRAME_MIB_T *)data)->RxCorrect   = WAN_MIB_TABLE[ndev].wrx_correct_pdu;
-        ((PTM_FRAME_MIB_T *)data)->TC_CrcError = WAN_MIB_TABLE[ndev].wrx_tccrc_err_pdu;
-        ((PTM_FRAME_MIB_T *)data)->RxDropped   = WAN_MIB_TABLE[ndev].wrx_nodesc_drop_pdu + WAN_MIB_TABLE[ndev].wrx_len_violation_drop_pdu;
-        ((PTM_FRAME_MIB_T *)data)->TxSend      = WAN_MIB_TABLE[ndev].wtx_total_pdu;
-        break;
-    case IFX_PTM_CFG_GET:
-        ((IFX_PTM_CFG_T *)data)->RxEthCrcPresent = CFG_ETH_EFMTC_CRC->rx_eth_crc_present;
-        ((IFX_PTM_CFG_T *)data)->RxEthCrcCheck   = CFG_ETH_EFMTC_CRC->rx_eth_crc_check;
-        ((IFX_PTM_CFG_T *)data)->RxTcCrcCheck    = CFG_ETH_EFMTC_CRC->rx_tc_crc_check;
-        ((IFX_PTM_CFG_T *)data)->RxTcCrcLen      = CFG_ETH_EFMTC_CRC->rx_tc_crc_len;
-        ((IFX_PTM_CFG_T *)data)->TxEthCrcGen     = CFG_ETH_EFMTC_CRC->tx_eth_crc_gen;
-        ((IFX_PTM_CFG_T *)data)->TxTcCrcGen      = CFG_ETH_EFMTC_CRC->tx_tc_crc_gen;
-        ((IFX_PTM_CFG_T *)data)->TxTcCrcLen      = CFG_ETH_EFMTC_CRC->tx_tc_crc_len;
-        break;
-    case IFX_PTM_CFG_SET:
-        CFG_ETH_EFMTC_CRC->rx_eth_crc_present   = ((IFX_PTM_CFG_T *)data)->RxEthCrcPresent ? 1 : 0;
-        CFG_ETH_EFMTC_CRC->rx_eth_crc_check     = ((IFX_PTM_CFG_T *)data)->RxEthCrcCheck ? 1 : 0;
-        if ( ((IFX_PTM_CFG_T *)data)->RxTcCrcCheck && (((IFX_PTM_CFG_T *)data)->RxTcCrcLen == 16 || ((IFX_PTM_CFG_T *)data)->RxTcCrcLen == 32) )
         {
-            CFG_ETH_EFMTC_CRC->rx_tc_crc_check  = 1;
-            CFG_ETH_EFMTC_CRC->rx_tc_crc_len    = ((IFX_PTM_CFG_T *)data)->RxTcCrcLen;
+            PTM_CW_IF_ENTRY_T tmp = {0};
+
+            tmp.ifRxNoIdleCodewords   = WAN_MIB_TABLE[ndev].wrx_nonidle_cw;
+            tmp.ifRxIdleCodewords     = WAN_MIB_TABLE[ndev].wrx_idle_cw;
+            tmp.ifRxCodingViolation   = WAN_MIB_TABLE[ndev].wrx_err_cw;
+            tmp.ifTxNoIdleCodewords   = 0;
+            tmp.ifTxIdleCodewords     = 0;
+
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
         }
-        else
+        break;
+    case IFX_PTM_MIB_FRAME_GET:
         {
-            CFG_ETH_EFMTC_CRC->rx_tc_crc_check  = 0;
-            CFG_ETH_EFMTC_CRC->rx_tc_crc_len    = 0;
+            PTM_FRAME_MIB_T tmp = {0};
+    
+            tmp.RxCorrect   = WAN_MIB_TABLE[ndev].wrx_correct_pdu;
+            tmp.TC_CrcError = WAN_MIB_TABLE[ndev].wrx_tccrc_err_pdu;
+            tmp.RxDropped   = WAN_MIB_TABLE[ndev].wrx_nodesc_drop_pdu + WAN_MIB_TABLE[ndev].wrx_len_violation_drop_pdu;
+            tmp.TxSend      = WAN_MIB_TABLE[ndev].wtx_total_pdu;
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
         }
-        CFG_ETH_EFMTC_CRC->tx_eth_crc_gen       = ((IFX_PTM_CFG_T *)data)->TxEthCrcGen ? 1 : 0;
-        if ( ((IFX_PTM_CFG_T *)data)->TxTcCrcGen && (((IFX_PTM_CFG_T *)data)->TxTcCrcLen == 16 || ((IFX_PTM_CFG_T *)data)->TxTcCrcLen == 32) )
+        break;
+    case IFX_PTM_CFG_GET:
         {
-            CFG_ETH_EFMTC_CRC->tx_tc_crc_gen    = 1;
-            CFG_ETH_EFMTC_CRC->tx_tc_crc_len    = ((IFX_PTM_CFG_T *)data)->TxTcCrcLen;
+            IFX_PTM_CFG_T tmp = {0};
+
+            tmp.RxEthCrcPresent = CFG_ETH_EFMTC_CRC->rx_eth_crc_present;
+            tmp.RxEthCrcCheck   = CFG_ETH_EFMTC_CRC->rx_eth_crc_check;
+            tmp.RxTcCrcCheck    = CFG_ETH_EFMTC_CRC->rx_tc_crc_check;
+            tmp.RxTcCrcLen      = CFG_ETH_EFMTC_CRC->rx_tc_crc_len;
+            tmp.TxEthCrcGen     = CFG_ETH_EFMTC_CRC->tx_eth_crc_gen;
+            tmp.TxTcCrcGen      = CFG_ETH_EFMTC_CRC->tx_tc_crc_gen;
+            tmp.TxTcCrcLen      = CFG_ETH_EFMTC_CRC->tx_tc_crc_len;
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
         }
-        else
+        break;
+    case IFX_PTM_CFG_SET:
         {
-            CFG_ETH_EFMTC_CRC->tx_tc_crc_gen    = 0;
-            CFG_ETH_EFMTC_CRC->tx_tc_crc_len    = 0;
+            IFX_PTM_CFG_T cfg = {0};
+
+            if (copy_from_user(&cfg, data, sizeof(cfg)))
+                return -EFAULT;
+
+            CFG_ETH_EFMTC_CRC->rx_eth_crc_present   = cfg.RxEthCrcPresent ? 1 : 0;
+            CFG_ETH_EFMTC_CRC->rx_eth_crc_check     = cfg.RxEthCrcCheck ? 1 : 0;
+            if ( cfg.RxTcCrcCheck && (cfg.RxTcCrcLen == 16 || cfg.RxTcCrcLen == 32) )
+            {
+                CFG_ETH_EFMTC_CRC->rx_tc_crc_check  = 1;
+                CFG_ETH_EFMTC_CRC->rx_tc_crc_len    = cfg.RxTcCrcLen;
+            }
+            else
+            {
+                CFG_ETH_EFMTC_CRC->rx_tc_crc_check  = 0;
+                CFG_ETH_EFMTC_CRC->rx_tc_crc_len    = 0;
+            }
+            CFG_ETH_EFMTC_CRC->tx_eth_crc_gen       = cfg.TxEthCrcGen ? 1 : 0;
+            if ( cfg.TxTcCrcGen && (cfg.TxTcCrcLen == 16 || cfg.TxTcCrcLen == 32) )
+            {
+                CFG_ETH_EFMTC_CRC->tx_tc_crc_gen    = 1;
+                CFG_ETH_EFMTC_CRC->tx_tc_crc_len    = cfg.TxTcCrcLen;
+            }
+            else
+            {
+                CFG_ETH_EFMTC_CRC->tx_tc_crc_gen    = 0;
+                CFG_ETH_EFMTC_CRC->tx_tc_crc_len    = 0;
+            }
         }
         break;
     default:
index f580105505f10a1b727138ef558bf4ebb9fd97f7..faa4ed6938b7e6175cd04c4980d9e1626b577f82 100644 (file)
@@ -390,14 +390,24 @@ static int ptm_ioctl(struct net_device *dev, struct ifreq *ifr, void __user *dat
 {
     ASSERT(dev == g_net_dev[0], "incorrect device");
 
+    if (!capable(CAP_NET_ADMIN))
+        return -EPERM;
+
     switch ( cmd )
     {
     case IFX_PTM_MIB_CW_GET:
-       ((PTM_CW_IF_ENTRY_T *)data)->ifRxNoIdleCodewords   = IFX_REG_R32(DREG_AR_CELL0) + IFX_REG_R32(DREG_AR_CELL1);
-        ((PTM_CW_IF_ENTRY_T *)data)->ifRxIdleCodewords     = IFX_REG_R32(DREG_AR_IDLE_CNT0) + IFX_REG_R32(DREG_AR_IDLE_CNT1);
-        ((PTM_CW_IF_ENTRY_T *)data)->ifRxCodingViolation   = IFX_REG_R32(DREG_AR_CVN_CNT0) + IFX_REG_R32(DREG_AR_CVN_CNT1) + IFX_REG_R32(DREG_AR_CVNP_CNT0) + IFX_REG_R32(DREG_AR_CVNP_CNT1);
-        ((PTM_CW_IF_ENTRY_T *)data)->ifTxNoIdleCodewords   = IFX_REG_R32(DREG_AT_CELL0) + IFX_REG_R32(DREG_AT_CELL1);
-        ((PTM_CW_IF_ENTRY_T *)data)->ifTxIdleCodewords     = IFX_REG_R32(DREG_AT_IDLE_CNT0) + IFX_REG_R32(DREG_AT_IDLE_CNT1);
+        {
+            PTM_CW_IF_ENTRY_T tmp = {0};
+
+            tmp.ifRxNoIdleCodewords   = IFX_REG_R32(DREG_AR_CELL0) + IFX_REG_R32(DREG_AR_CELL1);
+            tmp.ifRxIdleCodewords     = IFX_REG_R32(DREG_AR_IDLE_CNT0) + IFX_REG_R32(DREG_AR_IDLE_CNT1);
+            tmp.ifRxCodingViolation   = IFX_REG_R32(DREG_AR_CVN_CNT0) + IFX_REG_R32(DREG_AR_CVN_CNT1) + IFX_REG_R32(DREG_AR_CVNP_CNT0) + IFX_REG_R32(DREG_AR_CVNP_CNT1);
+            tmp.ifTxNoIdleCodewords   = IFX_REG_R32(DREG_AT_CELL0) + IFX_REG_R32(DREG_AT_CELL1);
+            tmp.ifTxIdleCodewords     = IFX_REG_R32(DREG_AT_IDLE_CNT0) + IFX_REG_R32(DREG_AT_IDLE_CNT1);
+
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
+        }
         break;
     case IFX_PTM_MIB_FRAME_GET:
        {
@@ -410,38 +420,50 @@ static int ptm_ioctl(struct net_device *dev, struct ifreq *ifr, void __user *dat
             for ( i = 0; i < 8; i++ )
                 tmp.TxSend    += WAN_TX_MIB_TABLE(i)->wtx_total_pdu;
 
-            *((PTM_FRAME_MIB_T *)data) = tmp;
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
         }
         break;
     case IFX_PTM_CFG_GET:
-       //  use bear channel 0 preemption gamma interface settings
-        ((IFX_PTM_CFG_T *)data)->RxEthCrcPresent = 1;
-        ((IFX_PTM_CFG_T *)data)->RxEthCrcCheck   = RX_GAMMA_ITF_CFG(0)->rx_eth_fcs_ver_dis == 0 ? 1 : 0;
-        ((IFX_PTM_CFG_T *)data)->RxTcCrcCheck    = RX_GAMMA_ITF_CFG(0)->rx_tc_crc_ver_dis == 0 ? 1 : 0;;
-        ((IFX_PTM_CFG_T *)data)->RxTcCrcLen      = RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size == 0 ? 0 : (RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size * 16);
-        ((IFX_PTM_CFG_T *)data)->TxEthCrcGen     = TX_GAMMA_ITF_CFG(0)->tx_eth_fcs_gen_dis == 0 ? 1 : 0;
-        ((IFX_PTM_CFG_T *)data)->TxTcCrcGen      = TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size == 0 ? 0 : 1;
-        ((IFX_PTM_CFG_T *)data)->TxTcCrcLen      = TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size == 0 ? 0 : (TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size * 16);
+        {
+            IFX_PTM_CFG_T tmp = {0};
+
+            //  use bear channel 0 preemption gamma interface settings
+            tmp.RxEthCrcPresent = 1;
+            tmp.RxEthCrcCheck   = RX_GAMMA_ITF_CFG(0)->rx_eth_fcs_ver_dis == 0 ? 1 : 0;
+            tmp.RxTcCrcCheck    = RX_GAMMA_ITF_CFG(0)->rx_tc_crc_ver_dis == 0 ? 1 : 0;
+            tmp.RxTcCrcLen      = RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size == 0 ? 0 : (RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size * 16);
+            tmp.TxEthCrcGen     = TX_GAMMA_ITF_CFG(0)->tx_eth_fcs_gen_dis == 0 ? 1 : 0;
+            tmp.TxTcCrcGen      = TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size == 0 ? 0 : 1;
+            tmp.TxTcCrcLen      = TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size == 0 ? 0 : (TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size * 16);
+
+            if (copy_to_user(data, &tmp, sizeof(tmp)))
+                return -EFAULT;
+        }
         break;
     case IFX_PTM_CFG_SET:
        {
+            IFX_PTM_CFG_T cfg;
             int i;
 
+            if (copy_from_user(&cfg, data, sizeof(cfg)))
+                return -EFAULT;
+
             for ( i = 0; i < 4; i++ ) {
-                RX_GAMMA_ITF_CFG(i)->rx_eth_fcs_ver_dis = ((IFX_PTM_CFG_T *)data)->RxEthCrcCheck ? 0 : 1;
+                RX_GAMMA_ITF_CFG(i)->rx_eth_fcs_ver_dis = cfg.RxEthCrcCheck ? 0 : 1;
 
-                RX_GAMMA_ITF_CFG(0)->rx_tc_crc_ver_dis = ((IFX_PTM_CFG_T *)data)->RxTcCrcCheck ? 0 : 1;
+                RX_GAMMA_ITF_CFG(0)->rx_tc_crc_ver_dis = cfg.RxTcCrcCheck ? 0 : 1;
 
-                switch ( ((IFX_PTM_CFG_T *)data)->RxTcCrcLen ) {
+                switch ( cfg.RxTcCrcLen ) {
                     case 16: RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size = 1; break;
                     case 32: RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size = 2; break;
                     default: RX_GAMMA_ITF_CFG(0)->rx_tc_crc_size = 0;
                 }
 
-                TX_GAMMA_ITF_CFG(0)->tx_eth_fcs_gen_dis = ((IFX_PTM_CFG_T *)data)->TxEthCrcGen ? 0 : 1;
+                TX_GAMMA_ITF_CFG(0)->tx_eth_fcs_gen_dis = cfg.TxEthCrcGen ? 0 : 1;
 
-                if ( ((IFX_PTM_CFG_T *)data)->TxTcCrcGen ) {
-                    switch ( ((IFX_PTM_CFG_T *)data)->TxTcCrcLen ) {
+                if ( cfg.TxTcCrcGen ) {
+                    switch ( cfg.TxTcCrcLen ) {
                         case 16: TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size = 1; break;
                         case 32: TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size = 2; break;
                         default: TX_GAMMA_ITF_CFG(0)->tx_tc_crc_size = 0;