Skip to content

Conversation

pvts-mat
Copy link
Contributor

@pvts-mat pvts-mat commented Feb 20, 2025

[LTS 8.6 RT]
CVE-2023-4623
VULN-6708

Problem

https://www.cve.org/CVERecord?id=CVE-2023-4623

A use-after-free vulnerability in the Linux kernel's net/sched: sch_hfsc (HFSC qdisc traffic control) component can be exploited to achieve local privilege escalation. If a class with a link-sharing curve (i.e. with the HFSC_FSC flag set) has a parent without a link-sharing curve, then init_vf() will call vttree_insert() on the parent, but vttree_remove() will be skipped in update_vf(). This leaves a dangling pointer that can cause a use-after-free.

Analysis and solution

A single commit was identified as a fix for this issue: b3d26c5702c7d6c45456326e56d2ccf3f103e60f net/sched: sch_hfsc: Ensure inner classes have fsc curve.

The solution consisted of rejecting the addition of a class with a link-sharing curve to the class without it (see Specific tests for details)

[root@ciqlts8_6-rt pvts]# (
    tc qdisc add dev lo root handle 1: hfsc default 10
    tc class add dev lo parent 1: classid 1:1 hfsc rt m2 100kbps
    tc class add dev lo parent 1:1 classid 1:10 hfsc ls m2 50kbps
    echo $?
)

Error: Invalid parent - parent class must have FSC.
2

The fix introduced a problem with existing network setup scripts for some users https://lore.kernel.org/all/[email protected]/:

I upgraded from 6.1.38 to 6.1.55 this morning and it broke my traffic shaping script, leaving me with a non-functional uplink on a remote router.

It was decided to fix the problem without breaking backwards compatibility https://lore.kernel.org/all/[email protected]/:

Setting 'rt' as a parent is incorrect and the man page is explicit about
it as it doesn't make sense 'qdisc wise'. Being able to set it has
always been wrong unfortunately…

Sure but unfortunately "we don't break backward compat" means
we can't really argue. It will take us more time to debate this
than to fix it (assuming we understand the initial problem).

Frankly one can even argue whether "exploitable by root / userns"
is more important than single user's init scripts breaking.
The "security" issues for root are dime a dozen.

The solution was to change the erroneous qdisc hierarchy to a correct one when the possible UAF condition was detected https://lore.kernel.org/all/[email protected]/:

As reported [1] disallowing 'rt' inner curves breaks already existing
scripts. Even though it doesn't make sense 'qdisc wise' users have been
relying on this behaviour since the qdisc inception.

We need users to update the scripts/applications to use 'sc' or 'ls'
as a inner curve instead of 'rt', but also avoid the UAF found by
Budimir, which was present since the qdisc inception.

The fix of the fix is given in the commit a13b67c9a015c4e21601ef9aa4ec9c5d972df1b4 net/sched: sch_hfsc: upgrade 'rt' to 'sc' when it becomes a inner curve

While the changes could be squashed into a single commit it was decided to retain the sequence of two commits for more straightforward LTS 8.6 RT - mainline patches correspondence.

The same solution was used already in other "version 8" branches centos8, fips-8-complaint/4.18.0-553.16.1, fips-8/4.18.0-553.16.1, rocky8_10, sig-cloud-8/4.18.0-553.22.1.el8_10, sig-cloud-8/4.18.0-553.33.1.el8_10, sig-cloud-8/4.18.0-553.36.1.el8_10:

git --no-pager log --decorate  --format="%h %cd %d %s" --date=short -n 2 ae71642ce

ae71642ce 2024-09-11  net/sched: sch_hfsc: upgrade 'rt' to 'sc' when it becomes a inner curve
1a61f0a5d 2024-09-11  net/sched: sch_hfsc: Ensure inner classes have fsc curve

as well as in ciqcbr7.9:

git --no-pager log --decorate  --format="%h %cd %d %s" --date=short -n 2 01ce15bfa

01ce15bfa 2024-10-09  net/sched: sch_hfsc: upgrade 'rt' to 'sc' when it becomes a inner curve
39170ba01 2024-10-09  net/sched: sch_hfsc: Ensure inner classes have fsc curve

kABI check: omitted (unstable ABI of RT kernels)

Boot test: passed

Refer to Specific tests for implicit boot test passing.

Kselftests: passed relative

Methodology

Source-compiled (e1a9851e6068a6ef800ec6b2b48a7c243882ed06) kselftests suite was used.

Coverage (including tests skipped during execution)

android, breakpoints, capabilities, core, cpu-hotplug, cpufreq, efivarfs, exec, filesystems, firmware, fpu, ftrace, futex, gpio, intel_pstate, ipc, kcmp, kvm, lib, livepatch, membarrier, memfd, memory-hotplug, mount, net, net/forwarding, net/mptcp, netfilter, nsfs, proc, pstore, ptrace, rseq, rtc, sgx, sigaltstack, size, splice, static_keys, sync, sysctl, tc-testing, timens, timers, tpm2, user, vm, x86, zram

Reference ciqlts8_6-rt (4d7a77d12ab8e3796e87dbf668480b65ea40a83b)

Five test runs were conducted on the reference kernel.
kselftests–src–ciqlts8_6-rt–run1.log
kselftests–src–ciqlts8_6-rt–run2.log
kselftests–src–ciqlts8_6-rt–run3.log
kselftests–src–ciqlts8_6-rt–run4.log
kselftests–src–ciqlts8_6-rt–run5.log

Patch ciqlts8_6-rt-CVE-2023-4623 (08c51c3ed947c0a2ae712c782b5cba1f4611528f)

Two test runs were conducted on the patched kernel.
kselftests–src–ciqlts8_6-rt-CVE-2023-4623–run2.log
kselftests–src–ciqlts8_6-rt-CVE-2023-4623–run1.log

Comparison

ktests.xsh  table --where "Summary = 'diff'"  kselftests*.log

Column    File
--------  -----------------------------------------------------
Status0   kselftests--src--ciqlts8_6-rt--run1.log
Status1   kselftests--src--ciqlts8_6-rt--run2.log
Status2   kselftests--src--ciqlts8_6-rt--run3.log
Status3   kselftests--src--ciqlts8_6-rt--run4.log
Status4   kselftests--src--ciqlts8_6-rt--run5.log
Status5   kselftests--src--ciqlts8_6-rt-CVE-2023-4623--run1.log
Status6   kselftests--src--ciqlts8_6-rt-CVE-2023-4623--run2.log

TestCase                   Status0  Status1  Status2  Status3  Status4  Status5  Status6  Summary
kvm:hardware_disable_test  pass     fail     pass     fail     fail     fail     pass     diff
net/mptcp:mptcp_join.sh    fail     pass     pass     pass     fail     fail     fail     diff
net:bareudp.sh             skip     skip     pass     pass     pass     pass     pass     diff
net:gro.sh                 pass     fail     pass     fail     fail     fail     pass     diff
net:ip_defrag.sh           fail     pass     pass     pass     pass     pass     pass     diff
net:reuseport_addr_any.sh  fail     fail     fail     fail     fail     fail     pass     diff
tc-testing:tdc.sh          fail     fail     pass     pass     pass     pass     pass     diff

Tests kvm:hardware_disable_test, net/mptcp:mptcp_join.sh, net:gro.sh, net:ip_defrag.sh, net:reuseport_addr_any.sh have already shown inconsistent behavior before https://docs.google.com/spreadsheets/d/1tUwJ2rV57cYZXh7momPtraSjZcHDjMYHLeHA3DYWrUU/edit?pli=1&gid=0#gid=0&range=D:D

The net:bareudp.sh test was skipped in the first two runs of the reference set because of the tc command missing in the system

# selftests: net: bareudp.sh
# ./bareudp.sh: line 205: tc: command not found
# Error: Setting up the testing environment failed.
ok 38 selftests: net: bareudp.sh # SKIP

The dependency was later solved with the iproute-tc package. Same for the tc-testing:tdc.sh test

# selftests: tc-testing: tdc.sh
# The specified tc path /sbin/tc does not exist.
# The specified tc path /sbin/tc does not exist.
not ok 1 selftests: tc-testing: tdc.sh # exit=1

Specific tests: passed

The potential UAF condition was found to be reproducible with the following tc commands sequence:

tc qdisc add dev lo root handle 1: hfsc default 10
# Inner hfsc class constructed with 'rt' - realtime service
tc class add dev lo parent 1: classid 1:1 hfsc rt m2 100kbps
# Leaf hfsc class constructed with 'ls' - linksharing service
tc class add dev lo parent 1:1 classid 1:10 hfsc ls m2 50kbps

The "100kbps", "50kbps" parts are arbitrary. What's important is the use of rt for the inner class and ls for the leaf class. While the exact UAF was not obtained the commands helped confirm the efficacy of the patch.

Reference

The incorrect qdisc hierarchy can be created without any guardrails.

[root@ciqlts8_6-rt pvts]# (
    tc qdisc add dev lo root handle 1: hfsc default 10
    tc class add dev lo parent 1: classid 1:1 hfsc rt m2 100kbps
    tc class add dev lo parent 1:1 classid 1:10 hfsc ls m2 50kbps
    echo $?
)
0
[root@ciqlts8_6-rt pvts]# tc -g class show dev lo
tc -g class show dev lo
+---(1:) hfsc 
     +---(1:1) hfsc rt m1 0bit d 0us m2 800Kbit 
          +---(1:10) hfsc ls m1 0bit d 0us m2 400Kbit 

Full logs:
fix-replicate–ciqlts8_6-rt.log

Patch

Creating the incorrect qdisc hierarchy raises a warning, but succeeds. Notice the type of inner class being sc instead of rt as shown by tc -g class show dev lo command.

[root@ciqlts8_6-rt pvts]# (
    tc qdisc add dev lo root handle 1: hfsc default 10
    tc class add dev lo parent 1: classid 1:1 hfsc rt m2 100kbps
    tc class add dev lo parent 1:1 classid 1:10 hfsc ls m2 50kbps
    echo $?
)
Warning: Forced curve change on parent 'rt' to 'sc'.
0
[root@ciqlts8_6-rt pvts]# tc -g class show dev lo
tc -g class show dev lo
+---(1:) hfsc 
     +---(1:1) hfsc sc m1 0bit d 0us m2 800Kbit 
          +---(1:10) hfsc ls m1 0bit d 0us m2 400Kbit 

Full logs:
fix-replicate–ciqlts8_6-rt-CVE-2023-4623.log

jira VULN-6708
cve CVE-2023-4623
commit-author Budimir Markovic <[email protected]>
commit b3d26c5

HFSC assumes that inner classes have an fsc curve, but it is currently
possible for classes without an fsc curve to become parents. This leads
to bugs including a use-after-free.

Don't allow non-root classes without HFSC_FSC to become parents.

Fixes: 1da177e ("Linux-2.6.12-rc2")
	Reported-by: Budimir Markovic <[email protected]>
	Signed-off-by: Budimir Markovic <[email protected]>
	Acked-by: Jamal Hadi Salim <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
	Signed-off-by: Jakub Kicinski <[email protected]>
(cherry picked from commit b3d26c5)
	Signed-off-by: Marcin Wcisło <[email protected]>
jira VULN-6708
cve CVE-2023-4623
commit-author Pedro Tammela <[email protected]>
commit a13b67c

Christian Theune says:
   I upgraded from 6.1.38 to 6.1.55 this morning and it broke my traffic shaping script,
   leaving me with a non-functional uplink on a remote router.

A 'rt' curve cannot be used as a inner curve (parent class), but we were
allowing such configurations since the qdisc was introduced. Such
configurations would trigger a UAF as Budimir explains:
   The parent will have vttree_insert() called on it in init_vf(),
   but will not have vttree_remove() called on it in update_vf()
   because it does not have the HFSC_FSC flag set.

The qdisc always assumes that inner classes have the HFSC_FSC flag set.
This is by design as it doesn't make sense 'qdisc wise' for an 'rt'
curve to be an inner curve.

Budimir's original patch disallows users to add classes with a 'rt'
parent, but this is too strict as it breaks users that have been using
'rt' as a inner class. Another approach, taken by this patch, is to
upgrade the inner 'rt' into a 'sc', warning the user in the process.
It avoids the UAF reported by Budimir while also being more permissive
to bad scripts/users/code using 'rt' as a inner class.

Users checking the `tc class ls [...]` or `tc class get [...]` dumps would
observe the curve change and are potentially breaking with this change.

v1->v2: https://lore.kernel.org/all/[email protected]/
- Correct 'Fixes' tag and merge with revert (Jakub)

	Cc: Christian Theune <[email protected]>
	Cc: Budimir Markovic <[email protected]>
Fixes: b3d26c5 ("net/sched: sch_hfsc: Ensure inner classes have fsc curve")
	Signed-off-by: Pedro Tammela <[email protected]>
	Acked-by: Jamal Hadi Salim <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
	Signed-off-by: Jakub Kicinski <[email protected]>
(cherry picked from commit a13b67c)
	Signed-off-by: Marcin Wcisło <[email protected]>
Copy link
Collaborator

@bmastbergen bmastbergen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🥌

Copy link

@gvrose8192 gvrose8192 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - Nice analysis. Thanks!

@gvrose8192 gvrose8192 merged commit 29049ba into ctrliq:ciqlts8_6-rt Feb 21, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants