diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index 6d2cb56595b12..d4383b5f0e9e4 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -847,8 +847,11 @@ void iscsit_dec_conn_usage_count(struct iscsi_conn *conn) spin_lock_bh(&conn->conn_usage_lock); conn->conn_usage_count--; - if (!conn->conn_usage_count && conn->conn_waiting_on_uc) + if (!conn->conn_usage_count && conn->conn_waiting_on_uc) { + spin_unlock_bh(&conn->conn_usage_lock); complete(&conn->conn_waiting_on_uc_comp); + return; + } spin_unlock_bh(&conn->conn_usage_lock); } diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e52660cd71326..5a20c7f7b0431 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3271,6 +3271,18 @@ skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) skb_headlen(skb), buffer); } +/* Variant of skb_header_pointer() where @offset is user-controlled + * and potentially negative. + */ +static inline void * __must_check +skb_header_pointer_careful(const struct sk_buff *skb, int offset, + int len, void *buffer) +{ + if (unlikely(offset < 0 && -offset > skb_headroom(skb))) + return NULL; + return skb_header_pointer(skb, offset, len, buffer); +} + /** * skb_needs_linearize - check if we need to linearize a given skb * depending on the given device features. diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 73e97f73447a7..c41839c37384e 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -157,10 +157,8 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, int toff = off + key->off + (off2 & key->offmask); __be32 *data, hdata; - if (skb_headroom(skb) + toff > INT_MAX) - goto out; - - data = skb_header_pointer(skb, toff, 4, &hdata); + data = skb_header_pointer_careful(skb, toff, 4, + &hdata); if (!data) goto out; if ((*data ^ key->val) & key->mask) { @@ -212,8 +210,9 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, if (ht->divisor) { __be32 *data, hdata; - data = skb_header_pointer(skb, off + n->sel.hoff, 4, - &hdata); + data = skb_header_pointer_careful(skb, + off + n->sel.hoff, + 4, &hdata); if (!data) goto out; sel = ht->divisor & u32_hash_fold(*data, &n->sel, @@ -227,7 +226,7 @@ static int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp, if (n->sel.flags & TC_U32_VAROFFSET) { __be16 *data, hdata; - data = skb_header_pointer(skb, + data = skb_header_pointer_careful(skb, off + n->sel.offoff, 2, &hdata); if (!data) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index f9724224790c0..bc5288198ae71 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -179,6 +179,11 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt) if (m->dev == dev) return -ELOOP; + if (sch->parent != TC_H_ROOT) { + NL_SET_ERR_MSG_MOD(extack, "teql can only be used as root"); + return -EOPNOTSUPP; + } + q->m = m; skb_queue_head_init(&q->q);