net/sched: sch_taprio: fix possible use-after-free
[platform/kernel/linux-rpi.git] / net / sched / sch_taprio.c
index a66398f..a76a2af 100644 (file)
@@ -66,6 +66,7 @@ struct taprio_sched {
        u32 flags;
        enum tk_offsets tk_offset;
        int clockid;
+       bool offloaded;
        atomic64_t picos_per_byte; /* Using picoseconds because for 10Gbps+
                                    * speeds it's sub-nanoseconds per byte
                                    */
@@ -417,7 +418,8 @@ static int taprio_enqueue_one(struct sk_buff *skb, struct Qdisc *sch,
 {
        struct taprio_sched *q = qdisc_priv(sch);
 
-       if (skb->sk && sock_flag(skb->sk, SOCK_TXTIME)) {
+       /* sk_flags are only safe to use on full sockets. */
+       if (skb->sk && sk_fullsock(skb->sk) && sock_flag(skb->sk, SOCK_TXTIME)) {
                if (!is_valid_interval(skb, sch))
                        return qdisc_drop(skb, sch, to_free);
        } else if (TXTIME_ASSIST_IS_ENABLED(q->flags)) {
@@ -1277,6 +1279,8 @@ static int taprio_enable_offload(struct net_device *dev,
                goto done;
        }
 
+       q->offloaded = true;
+
 done:
        taprio_offload_free(offload);
 
@@ -1291,12 +1295,9 @@ static int taprio_disable_offload(struct net_device *dev,
        struct tc_taprio_qopt_offload *offload;
        int err;
 
-       if (!FULL_OFFLOAD_IS_ENABLED(q->flags))
+       if (!q->offloaded)
                return 0;
 
-       if (!ops->ndo_setup_tc)
-               return -EOPNOTSUPP;
-
        offload = taprio_offload_alloc(0);
        if (!offload) {
                NL_SET_ERR_MSG(extack,
@@ -1312,6 +1313,8 @@ static int taprio_disable_offload(struct net_device *dev,
                goto out;
        }
 
+       q->offloaded = false;
+
 out:
        taprio_offload_free(offload);
 
@@ -1629,13 +1632,13 @@ static void taprio_reset(struct Qdisc *sch)
        int i;
 
        hrtimer_cancel(&q->advance_timer);
+       qdisc_synchronize(sch);
+
        if (q->qdiscs) {
                for (i = 0; i < dev->num_tx_queues; i++)
                        if (q->qdiscs[i])
                                qdisc_reset(q->qdiscs[i]);
        }
-       sch->qstats.backlog = 0;
-       sch->q.qlen = 0;
 }
 
 static void taprio_destroy(struct Qdisc *sch)
@@ -1652,6 +1655,7 @@ static void taprio_destroy(struct Qdisc *sch)
         * happens in qdisc_create(), after taprio_init() has been called.
         */
        hrtimer_cancel(&q->advance_timer);
+       qdisc_synchronize(sch);
 
        taprio_disable_offload(dev, q, NULL);
 
@@ -1947,12 +1951,14 @@ start_error:
 
 static struct Qdisc *taprio_leaf(struct Qdisc *sch, unsigned long cl)
 {
-       struct netdev_queue *dev_queue = taprio_queue_get(sch, cl);
+       struct taprio_sched *q = qdisc_priv(sch);
+       struct net_device *dev = qdisc_dev(sch);
+       unsigned int ntx = cl - 1;
 
-       if (!dev_queue)
+       if (ntx >= dev->num_tx_queues)
                return NULL;
 
-       return dev_queue->qdisc_sleeping;
+       return q->qdiscs[ntx];
 }
 
 static unsigned long taprio_find(struct Qdisc *sch, u32 classid)