Message ID | 20200527175808.peynuk7a6webysv3@kili.mountain (mailing list archive) |
---|---|
State | Changes Requested |
Delegated to: | Kalle Valo |
Headers | show |
Series | [v2] airo: Fix read overflows sending packets | expand |
On Wed, May 27, 2020 at 10:58 AM Dan Carpenter <dan.carpenter@oracle.com> wrote: > > The problem is that we always copy a minimum of ETH_ZLEN (60) bytes from > skb->data even when skb->len is less than ETH_ZLEN so it leads to a read > overflow. > > The fix is to pad skb->data with zeroes so that it's never less than > ETH_ZLEN bytes. > > Cc: <stable@vger.kernel.org> > Reported-by: Hu Jiahui <kirin.say@gmail.com> > Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> > --- > v2: remove an unnecessary if statement > increment the ->tx_dropped count on failure > fix found two more instances of the same bug. > fix typo in the "Cc: <stable@vger.kernel.org>" tag > > drivers/net/wireless/cisco/airo.c | 26 ++++++++++++++++---------- > 1 file changed, 16 insertions(+), 10 deletions(-) > > diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c > index 8363f91df7ea..c80712e61ccf 100644 > --- a/drivers/net/wireless/cisco/airo.c > +++ b/drivers/net/wireless/cisco/airo.c > @@ -1925,6 +1925,11 @@ static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, > airo_print_err(dev->name, "%s: skb == NULL!",__func__); > return NETDEV_TX_OK; > } > + if (skb_padto(skb, ETH_ZLEN)) { > + dev->stats.tx_dropped++; > + return NETDEV_TX_OK; > + } > + > npacks = skb_queue_len (&ai->txq); > > if (npacks >= MAXTXQ - 1) { > @@ -1975,8 +1980,7 @@ static int mpi_send_packet (struct net_device *dev) > return 0; > } > > - /* check min length*/ > - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; Note that skb_padto() does not change skb->len So this patch could trigger a hardware bug. > + len = skb->len; > buffer = skb->data; > > ai->txfids[0].tx_desc.offset = 0; > @@ -2118,7 +2122,6 @@ static void airo_end_xmit(struct net_device *dev) { > static netdev_tx_t airo_start_xmit(struct sk_buff *skb, > struct net_device *dev) > { > - s16 len; > int i, j; > struct airo_info *priv = dev->ml_priv; > u32 *fids = priv->fids; > @@ -2127,6 +2130,10 @@ static netdev_tx_t airo_start_xmit(struct sk_buff *skb, > airo_print_err(dev->name, "%s: skb == NULL!", __func__); > return NETDEV_TX_OK; > } > + if (skb_padto(skb, ETH_ZLEN)) { > + dev->stats.tx_dropped++; > + return NETDEV_TX_OK; > + } > > /* Find a vacant FID */ > for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); > @@ -2140,10 +2147,8 @@ static netdev_tx_t airo_start_xmit(struct sk_buff *skb, > return NETDEV_TX_BUSY; > } > } > - /* check min length*/ > - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; > /* Mark fid as used & save length for later */ > - fids[i] |= (len << 16); > + fids[i] |= (skb->len << 16); > priv->xmit.skb = skb; > priv->xmit.fid = i; > if (down_trylock(&priv->sem) != 0) { > @@ -2185,7 +2190,6 @@ static void airo_end_xmit11(struct net_device *dev) { > static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, > struct net_device *dev) > { > - s16 len; > int i, j; > struct airo_info *priv = dev->ml_priv; > u32 *fids = priv->fids; > @@ -2201,6 +2205,10 @@ static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, > airo_print_err(dev->name, "%s: skb == NULL!", __func__); > return NETDEV_TX_OK; > } > + if (skb_padto(skb, ETH_ZLEN)) { > + dev->stats.tx_dropped++; > + return NETDEV_TX_OK; > + } > > /* Find a vacant FID */ > for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); > @@ -2214,10 +2222,8 @@ static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, > return NETDEV_TX_BUSY; > } > } > - /* check min length*/ > - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; > /* Mark fid as used & save length for later */ > - fids[i] |= (len << 16); > + fids[i] |= (skb->len << 16); > priv->xmit11.skb = skb; > priv->xmit11.fid = i; > if (down_trylock(&priv->sem) != 0) { > -- > 2.11.0 >
diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 8363f91df7ea..c80712e61ccf 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -1925,6 +1925,11 @@ static netdev_tx_t mpi_start_xmit(struct sk_buff *skb, airo_print_err(dev->name, "%s: skb == NULL!",__func__); return NETDEV_TX_OK; } + if (skb_padto(skb, ETH_ZLEN)) { + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } + npacks = skb_queue_len (&ai->txq); if (npacks >= MAXTXQ - 1) { @@ -1975,8 +1980,7 @@ static int mpi_send_packet (struct net_device *dev) return 0; } - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + len = skb->len; buffer = skb->data; ai->txfids[0].tx_desc.offset = 0; @@ -2118,7 +2122,6 @@ static void airo_end_xmit(struct net_device *dev) { static netdev_tx_t airo_start_xmit(struct sk_buff *skb, struct net_device *dev) { - s16 len; int i, j; struct airo_info *priv = dev->ml_priv; u32 *fids = priv->fids; @@ -2127,6 +2130,10 @@ static netdev_tx_t airo_start_xmit(struct sk_buff *skb, airo_print_err(dev->name, "%s: skb == NULL!", __func__); return NETDEV_TX_OK; } + if (skb_padto(skb, ETH_ZLEN)) { + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } /* Find a vacant FID */ for( i = 0; i < MAX_FIDS / 2 && (fids[i] & 0xffff0000); i++ ); @@ -2140,10 +2147,8 @@ static netdev_tx_t airo_start_xmit(struct sk_buff *skb, return NETDEV_TX_BUSY; } } - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); + fids[i] |= (skb->len << 16); priv->xmit.skb = skb; priv->xmit.fid = i; if (down_trylock(&priv->sem) != 0) { @@ -2185,7 +2190,6 @@ static void airo_end_xmit11(struct net_device *dev) { static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, struct net_device *dev) { - s16 len; int i, j; struct airo_info *priv = dev->ml_priv; u32 *fids = priv->fids; @@ -2201,6 +2205,10 @@ static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, airo_print_err(dev->name, "%s: skb == NULL!", __func__); return NETDEV_TX_OK; } + if (skb_padto(skb, ETH_ZLEN)) { + dev->stats.tx_dropped++; + return NETDEV_TX_OK; + } /* Find a vacant FID */ for( i = MAX_FIDS / 2; i < MAX_FIDS && (fids[i] & 0xffff0000); i++ ); @@ -2214,10 +2222,8 @@ static netdev_tx_t airo_start_xmit11(struct sk_buff *skb, return NETDEV_TX_BUSY; } } - /* check min length*/ - len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* Mark fid as used & save length for later */ - fids[i] |= (len << 16); + fids[i] |= (skb->len << 16); priv->xmit11.skb = skb; priv->xmit11.fid = i; if (down_trylock(&priv->sem) != 0) {
The problem is that we always copy a minimum of ETH_ZLEN (60) bytes from skb->data even when skb->len is less than ETH_ZLEN so it leads to a read overflow. The fix is to pad skb->data with zeroes so that it's never less than ETH_ZLEN bytes. Cc: <stable@vger.kernel.org> Reported-by: Hu Jiahui <kirin.say@gmail.com> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> --- v2: remove an unnecessary if statement increment the ->tx_dropped count on failure fix found two more instances of the same bug. fix typo in the "Cc: <stable@vger.kernel.org>" tag drivers/net/wireless/cisco/airo.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)