netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
@ 2025-01-11 15:14 Jamal Hadi Salim
  2025-01-15  1:26 ` Jakub Kicinski
  0 siblings, 1 reply; 9+ messages in thread
From: Jamal Hadi Salim @ 2025-01-11 15:14 UTC (permalink / raw)
  To: netdev; +Cc: jiri, xiyou.wangcong, davem, edumazet, kuba, security, nnamrec

Lion Ackermann was able to create a UAF which can be abused for privilege
escalation with the following script

Step 1. create root qdisc
tc qdisc add dev lo root handle 1:0 drr

step2. a class for packet aggregation do demonstrate uaf
tc class add dev lo classid 1:1 drr

step3. a class for nesting
tc class add dev lo classid 1:2 drr

step4. a class to graft qdisc to
tc class add dev lo classid 1:3 drr

step5.
tc qdisc add dev lo parent 1:1 handle 2:0 plug limit 1024

step6.
tc qdisc add dev lo parent 1:2 handle 3:0 drr

step7.
tc class add dev lo classid 3:1 drr

step 8.
tc qdisc add dev lo parent 3:1 handle 4:0 pfifo

step 9. Display the class/qdisc layout

tc class ls dev lo
 class drr 1:1 root leaf 2: quantum 64Kb
 class drr 1:2 root leaf 3: quantum 64Kb
 class drr 3:1 root leaf 4: quantum 64Kb

tc qdisc ls
 qdisc drr 1: dev lo root refcnt 2
 qdisc plug 2: dev lo parent 1:1
 qdisc pfifo 4: dev lo parent 3:1 limit 1000p
 qdisc drr 3: dev lo parent 1:2

step10. trigger the bug <=== prevented by this patch
tc qdisc replace dev lo parent 1:3 handle 4:0

step 11. Redisplay again the qdiscs/classes

tc class ls dev lo
 class drr 1:1 root leaf 2: quantum 64Kb
 class drr 1:2 root leaf 3: quantum 64Kb
 class drr 1:3 root leaf 4: quantum 64Kb
 class drr 3:1 root leaf 4: quantum 64Kb

tc qdisc ls
 qdisc drr 1: dev lo root refcnt 2
 qdisc plug 2: dev lo parent 1:1
 qdisc pfifo 4: dev lo parent 3:1 refcnt 2 limit 1000p
 qdisc drr 3: dev lo parent 1:2

Observe that a) parent for 4:0 does not change despite the replace request.
There can only be one parent.  b) refcount has gone up by two for 4:0 and
c) both class 1:3 and 3:1 are pointing to it.

Step 12.  send one packet to plug
echo "" | socat -u STDIN UDP4-DATAGRAM:127.0.0.1:8888,priority=$((0x10001))
step13.  send one packet to the grafted fifo
echo "" | socat -u STDIN UDP4-DATAGRAM:127.0.0.1:8888,priority=$((0x10003))

step14. lets trigger the uaf
tc class delete dev lo classid 1:3
tc class delete dev lo classid 1:1

The semantics of "replace" is for a del/add _on the same node_ and not
a delete from one node(3:1) and add to another node (1:3) as in step10.
While we could "fix" with a more complex approach there could be
consequences to expectations so the patch takes the preventive approach of
"disallow such config".

Joint work with Lion Ackermann <nnamrec@gmail.com>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/sched/sch_api.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 300430b8c4d2..79e0b5c919a9 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1612,7 +1612,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 	struct nlattr *tca[TCA_MAX + 1];
 	struct net_device *dev;
 	u32 clid;
-	struct Qdisc *q, *p;
+	struct Qdisc *leaf_q, *q, *p;
 	int err;
 
 replay:
@@ -1624,7 +1624,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 
 	tcm = nlmsg_data(n);
 	clid = tcm->tcm_parent;
-	q = p = NULL;
+	leaf_q = q = p = NULL;
 
 	dev = __dev_get_by_index(net, tcm->tcm_ifindex);
 	if (!dev)
@@ -1640,6 +1640,7 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 					return -ENOENT;
 				}
 				q = qdisc_leaf(p, clid);
+				leaf_q = q;
 			} else if (dev_ingress_queue_create(dev)) {
 				q = rtnl_dereference(dev_ingress_queue(dev)->qdisc_sleeping);
 			}
@@ -1673,6 +1674,11 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 					NL_SET_ERR_MSG(extack, "Invalid qdisc name");
 					return -EINVAL;
 				}
+				if (leaf_q && leaf_q->parent != q->parent) {
+					NL_SET_ERR_MSG(extack, "Invalid Parent for operation");
+					return -EINVAL;
+				}
+
 				if (q->flags & TCQ_F_INGRESS) {
 					NL_SET_ERR_MSG(extack,
 						       "Cannot regraft ingress or clsact Qdiscs");
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-11 15:14 [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another Jamal Hadi Salim
@ 2025-01-15  1:26 ` Jakub Kicinski
  2025-01-15 14:15   ` Jamal Hadi Salim
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2025-01-15  1:26 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Sat, 11 Jan 2025 10:14:55 -0500 Jamal Hadi Salim wrote:
> The semantics of "replace" is for a del/add _on the same node_ and not
> a delete from one node(3:1) and add to another node (1:3) as in step10.
> While we could "fix" with a more complex approach there could be
> consequences to expectations so the patch takes the preventive approach of
> "disallow such config".

Your explanation reads like you want to prevent a qdisc changing
from one parent to another.

> +				if (leaf_q && leaf_q->parent != q->parent) {
> +					NL_SET_ERR_MSG(extack, "Invalid Parent for operation");
> +					return -EINVAL;
> +				}

But this test looks at the full parent path, not the major.
So the only case you allow is replacing the node.. with itself?

Did you mean to wrap these in TC_H_MAJ() || the parent comparison 
is redundant || I misunderstand?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15  1:26 ` Jakub Kicinski
@ 2025-01-15 14:15   ` Jamal Hadi Salim
  2025-01-15 14:36     ` Jakub Kicinski
  0 siblings, 1 reply; 9+ messages in thread
From: Jamal Hadi Salim @ 2025-01-15 14:15 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Tue, Jan 14, 2025 at 8:26 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Sat, 11 Jan 2025 10:14:55 -0500 Jamal Hadi Salim wrote:
> > The semantics of "replace" is for a del/add _on the same node_ and not
> > a delete from one node(3:1) and add to another node (1:3) as in step10.
> > While we could "fix" with a more complex approach there could be
> > consequences to expectations so the patch takes the preventive approach of
> > "disallow such config".
>
> Your explanation reads like you want to prevent a qdisc changing
> from one parent to another.
>

Yes.

> > +                             if (leaf_q && leaf_q->parent != q->parent) {
> > +                                     NL_SET_ERR_MSG(extack, "Invalid Parent for operation");
> > +                                     return -EINVAL;
> > +                             }
>
> But this test looks at the full parent path, not the major.
> So the only case you allow is replacing the node.. with itself?
>

Yes.

> Did you mean to wrap these in TC_H_MAJ() || the parent comparison
> is redundant || I misunderstand?

I may be missing something - what does TC_H_MAJ() provide?
The 3:1 and 1:3 in that example are both descendants of the same
parent. It could have been 1:3 vs 1:2 and the same rules would apply.

cheers,
jamal

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15 14:15   ` Jamal Hadi Salim
@ 2025-01-15 14:36     ` Jakub Kicinski
  2025-01-15 14:53       ` Jamal Hadi Salim
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2025-01-15 14:36 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, 15 Jan 2025 09:15:31 -0500 Jamal Hadi Salim wrote:
> > On Sat, 11 Jan 2025 10:14:55 -0500 Jamal Hadi Salim wrote:  
> > > The semantics of "replace" is for a del/add _on the same node_ and not
> > > a delete from one node(3:1) and add to another node (1:3) as in step10.
> > > While we could "fix" with a more complex approach there could be
> > > consequences to expectations so the patch takes the preventive approach of
> > > "disallow such config".  
> >
> > Your explanation reads like you want to prevent a qdisc changing
> > from one parent to another.
> 
> Yes.

In the selftest with mq Victor updated I'd say we're not changing
the parent. We replace one child of mq with another.
TC noobs would say mq is the parent.

> > > +                             if (leaf_q && leaf_q->parent != q->parent) {
> > > +                                     NL_SET_ERR_MSG(extack, "Invalid Parent for operation");
> > > +                                     return -EINVAL;
> > > +                             }  
> >
> > But this test looks at the full parent path, not the major.
> > So the only case you allow is replacing the node.. with itself?
> >  
> 
> Yes.
> 
> > Did you mean to wrap these in TC_H_MAJ() || the parent comparison
> > is redundant || I misunderstand?  
> 
> I may be missing something - what does TC_H_MAJ() provide?
> The 3:1 and 1:3 in that example are both descendants of the same
> parent. It could have been 1:3 vs 1:2 and the same rules would apply.

Let me flip the question. What qdisc movement / grafts are you intending
to still support?

From the report it sounds like we don't want to support _any_ movement 
of existing qdiscs within the hierarchy. Only purpose of graft would 
be to install a new / fresh qdisc as a child.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15 14:36     ` Jakub Kicinski
@ 2025-01-15 14:53       ` Jamal Hadi Salim
  2025-01-15 15:51         ` Jakub Kicinski
  0 siblings, 1 reply; 9+ messages in thread
From: Jamal Hadi Salim @ 2025-01-15 14:53 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, Jan 15, 2025 at 9:36 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 15 Jan 2025 09:15:31 -0500 Jamal Hadi Salim wrote:
> > > On Sat, 11 Jan 2025 10:14:55 -0500 Jamal Hadi Salim wrote:
> > > > The semantics of "replace" is for a del/add _on the same node_ and not
> > > > a delete from one node(3:1) and add to another node (1:3) as in step10.
> > > > While we could "fix" with a more complex approach there could be
> > > > consequences to expectations so the patch takes the preventive approach of
> > > > "disallow such config".
> > >
> > > Your explanation reads like you want to prevent a qdisc changing
> > > from one parent to another.
> >
> > Yes.
>
> In the selftest with mq Victor updated I'd say we're not changing
> the parent. We replace one child of mq with another.
> TC noobs would say mq is the parent.

Yeah, Victor's test was to reduce the number of lines changed. IIRC,
there was an _child_assert which would have to change to a different
number given mq doesnt destroy the manually created qdiscs.

You can replace a queue attributes (eg queue size) or its algorithm
(example change from pfifo to bfifo) in place - that doesnt change.
What the patch avoids is taking that queue and moving it elsewhere or
having it "shared"; that puts it in the funny state that was used in
the exploit.
That was the ambiguity i was talking about in the earlier email.

> > > > +                             if (leaf_q && leaf_q->parent != q->parent) {
> > > > +                                     NL_SET_ERR_MSG(extack, "Invalid Parent for operation");
> > > > +                                     return -EINVAL;
> > > > +                             }
> > >
> > > But this test looks at the full parent path, not the major.
> > > So the only case you allow is replacing the node.. with itself?
> > >
> >
> > Yes.
> >
> > > Did you mean to wrap these in TC_H_MAJ() || the parent comparison
> > > is redundant || I misunderstand?
> >
> > I may be missing something - what does TC_H_MAJ() provide?
> > The 3:1 and 1:3 in that example are both descendants of the same
> > parent. It could have been 1:3 vs 1:2 and the same rules would apply.
>
> Let me flip the question. What qdisc movement / grafts are you intending
> to still support?
>

Grafting essentially boils down to a del/add of a qdisc. The
ambiguities: Does it mean deleting it from one hierachy point and
adding it to another point? Or does it mean not deleting it from the
first location but making it available in the other one?

> From the report it sounds like we don't want to support _any_ movement
> of existing qdiscs within the hierarchy. Only purpose of graft would
> be to install a new / fresh qdisc as a child.

That sounded like the safest approach. If there is a practical use for
moving queues around (I am not aware of any, doesnt mean there is no
practical use) then we can do the much bigger surgery.

cheers,
jamal

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15 14:53       ` Jamal Hadi Salim
@ 2025-01-15 15:51         ` Jakub Kicinski
  2025-01-15 20:39           ` Jamal Hadi Salim
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2025-01-15 15:51 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, 15 Jan 2025 09:53:27 -0500 Jamal Hadi Salim wrote:
> > > I may be missing something - what does TC_H_MAJ() provide?
> > > The 3:1 and 1:3 in that example are both descendants of the same
> > > parent. It could have been 1:3 vs 1:2 and the same rules would apply.  
> >
> > Let me flip the question. What qdisc movement / grafts are you intending
> > to still support?
> >  
> 
> Grafting essentially boils down to a del/add of a qdisc. The
> ambiguities: Does it mean deleting it from one hierachy point and
> adding it to another point? Or does it mean not deleting it from the
> first location but making it available in the other one?
> 
> > From the report it sounds like we don't want to support _any_ movement
> > of existing qdiscs within the hierarchy. Only purpose of graft would
> > be to install a new / fresh qdisc as a child.  
> 
> That sounded like the safest approach. If there is a practical use for
> moving queues around (I am not aware of any, doesnt mean there is no
> practical use) then we can do the much bigger surgery.

So coming back to the code I would have expected the patch to look
something along the lines of:

diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 300430b8c4d2..fac9c946a4c7 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1664,6 +1664,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
 				q = qdisc_lookup(dev, tcm->tcm_handle);
 				if (!q)
 					goto create_n_graft;
+				if (q->parent != tcm->tcm_parent) {
+					NL_SET_ERR_MSG(extack, "Cannot move an existing qdisc to a different parent");
+					return -EINVAL;
+				}
 				if (n->nlmsg_flags & NLM_F_EXCL) {
 					NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot override");
 					return -EEXIST;


Whether a real (non-default) leaf already existed in that spot 
is not important..

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15 15:51         ` Jakub Kicinski
@ 2025-01-15 20:39           ` Jamal Hadi Salim
  2025-01-16  1:33             ` Jakub Kicinski
  0 siblings, 1 reply; 9+ messages in thread
From: Jamal Hadi Salim @ 2025-01-15 20:39 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, Jan 15, 2025 at 10:51 AM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 15 Jan 2025 09:53:27 -0500 Jamal Hadi Salim wrote:
> > > > I may be missing something - what does TC_H_MAJ() provide?
> > > > The 3:1 and 1:3 in that example are both descendants of the same
> > > > parent. It could have been 1:3 vs 1:2 and the same rules would apply.
> > >
> > > Let me flip the question. What qdisc movement / grafts are you intending
> > > to still support?
> > >
> >
> > Grafting essentially boils down to a del/add of a qdisc. The
> > ambiguities: Does it mean deleting it from one hierachy point and
> > adding it to another point? Or does it mean not deleting it from the
> > first location but making it available in the other one?
> >
> > > From the report it sounds like we don't want to support _any_ movement
> > > of existing qdiscs within the hierarchy. Only purpose of graft would
> > > be to install a new / fresh qdisc as a child.
> >
> > That sounded like the safest approach. If there is a practical use for
> > moving queues around (I am not aware of any, doesnt mean there is no
> > practical use) then we can do the much bigger surgery.
>
> So coming back to the code I would have expected the patch to look
> something along the lines of:
>
> diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> index 300430b8c4d2..fac9c946a4c7 100644
> --- a/net/sched/sch_api.c
> +++ b/net/sched/sch_api.c
> @@ -1664,6 +1664,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
>                                 q = qdisc_lookup(dev, tcm->tcm_handle);
>                                 if (!q)
>                                         goto create_n_graft;
> +                               if (q->parent != tcm->tcm_parent) {
> +                                       NL_SET_ERR_MSG(extack, "Cannot move an existing qdisc to a different parent");
> +                                       return -EINVAL;
> +                               }

Yes, this should work as well - doesnt have to save the leaf_q, so more optimal.
Please send the patch.

cheers,
jamal
>                                 if (n->nlmsg_flags & NLM_F_EXCL) {
>                                         NL_SET_ERR_MSG(extack, "Exclusivity flag on, cannot override");
>                                         return -EEXIST;
>
>
> Whether a real (non-default) leaf already existed in that spot
> is not important..

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-15 20:39           ` Jamal Hadi Salim
@ 2025-01-16  1:33             ` Jakub Kicinski
  2025-01-16 13:34               ` Jamal Hadi Salim
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Kicinski @ 2025-01-16  1:33 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, 15 Jan 2025 15:39:43 -0500 Jamal Hadi Salim wrote:
> > diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> > index 300430b8c4d2..fac9c946a4c7 100644
> > --- a/net/sched/sch_api.c
> > +++ b/net/sched/sch_api.c
> > @@ -1664,6 +1664,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
> >                                 q = qdisc_lookup(dev, tcm->tcm_handle);
> >                                 if (!q)
> >                                         goto create_n_graft;
> > +                               if (q->parent != tcm->tcm_parent) {
> > +                                       NL_SET_ERR_MSG(extack, "Cannot move an existing qdisc to a different parent");
> > +                                       return -EINVAL;
> > +                               }  
> 
> Yes, this should work as well - doesnt have to save the leaf_q, so more optimal.
> Please send the patch.

I'm gonna keep your commit and just post a v4, hope that's okay.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another
  2025-01-16  1:33             ` Jakub Kicinski
@ 2025-01-16 13:34               ` Jamal Hadi Salim
  0 siblings, 0 replies; 9+ messages in thread
From: Jamal Hadi Salim @ 2025-01-16 13:34 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: netdev, jiri, xiyou.wangcong, davem, edumazet, security, nnamrec

On Wed, Jan 15, 2025 at 8:33 PM Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Wed, 15 Jan 2025 15:39:43 -0500 Jamal Hadi Salim wrote:
> > > diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> > > index 300430b8c4d2..fac9c946a4c7 100644
> > > --- a/net/sched/sch_api.c
> > > +++ b/net/sched/sch_api.c
> > > @@ -1664,6 +1664,10 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n,
> > >                                 q = qdisc_lookup(dev, tcm->tcm_handle);
> > >                                 if (!q)
> > >                                         goto create_n_graft;
> > > +                               if (q->parent != tcm->tcm_parent) {
> > > +                                       NL_SET_ERR_MSG(extack, "Cannot move an existing qdisc to a different parent");
> > > +                                       return -EINVAL;
> > > +                               }
> >
> > Yes, this should work as well - doesnt have to save the leaf_q, so more optimal.
> > Please send the patch.
>
> I'm gonna keep your commit and just post a v4, hope that's okay.

Thanks!

cheers,
jamal

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-01-16 13:34 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-11 15:14 [PATCH net 1/1 v3] net: sched: Disallow replacing of child qdisc from one parent to another Jamal Hadi Salim
2025-01-15  1:26 ` Jakub Kicinski
2025-01-15 14:15   ` Jamal Hadi Salim
2025-01-15 14:36     ` Jakub Kicinski
2025-01-15 14:53       ` Jamal Hadi Salim
2025-01-15 15:51         ` Jakub Kicinski
2025-01-15 20:39           ` Jamal Hadi Salim
2025-01-16  1:33             ` Jakub Kicinski
2025-01-16 13:34               ` Jamal Hadi Salim

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).