Skip to content

[LTS 8.6] net/ulp: use consistent error code when blocking ULP #282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2025

Conversation

pvts-mat
Copy link
Contributor

[LTS 8.6]
CVE-2023-0461
VULN-3655

Problem

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

There is a use-after-free vulnerability in the Linux Kernel which can be exploited to achieve local privilege escalation. To reach the vulnerability kernel configuration flag CONFIG_TLS or CONFIG_XFRM_ESPINTCP has to be configured, but the operation does not require any privilege. There is a use-after-free bug of icsk_ulp_data of a struct inet_connection_sock. When CONFIG_TLS is enabled, user can install a tls context (struct tls_context) on a connected tcp socket. The context is not cleared if this socket is disconnected and reused as a listener. If a new socket is created from the listener, the context is inherited and vulnerable. The setsockopt TCP_ULP operation does not require any privilege. We recommend upgrading past commit 2c02d41

Applicability

The CONFIG_TLS option mentioned as sufficient prerequisite for the bug is present in all configurations of ciqlts8_6:

$ grep 'CONFIG_TLS\b' configs/kernel*.config

configs/kernel-aarch64-debug.config:CONFIG_TLS=m
configs/kernel-aarch64.config:CONFIG_TLS=m
configs/kernel-ppc64le-debug.config:CONFIG_TLS=m
configs/kernel-ppc64le.config:CONFIG_TLS=m
configs/kernel-s390x-debug.config:CONFIG_TLS=m
configs/kernel-s390x-zfcpdump.config:CONFIG_TLS=m
configs/kernel-s390x.config:CONFIG_TLS=m
configs/kernel-x86_64-debug.config:CONFIG_TLS=m
configs/kernel-x86_64.config:CONFIG_TLS=m

However, CVE-2023-0461 has already been addressed for LTS 8.6 in the commit 68e4adc, so this specific vulnerability should not apply to ciqlts8_6 anymore. This PR was created nevertheless to close some gaps and finish the patch. See below.

Analysis and solution

Assessment

The patch is given in mainline in 2c02d41 commit. It was backported onto 7 stable branches which, along with the mainline, can be categorized into 3 groups based on their diffs

Additionally, the patch is present in Rocky Linux for (at least) 4 versions LTS 8.6, LTS 8.8, LTS 9.2, LTS 9.4, contained in 3 commits, each representing a class in itself.

  • Solution D

    68e4adc LTS 8.6 (4.18)
  • Solution E

    f950f23 LTS 8.8 (4.18)
  • Solution F

    0fd4c51 LTS 9.2 (5.14), LTS 9.4

The differences occur along four axes:

  • W: inet_ulp_can_listen function definition

    • 1: Lack of icsk->icsk_ulp_ops->clone in the condition check

      static int inet_ulp_can_listen(const struct sock *sk)
      {
      	const struct inet_connection_sock *icsk = inet_csk(sk);
      
      	if (icsk->icsk_ulp_ops)
      		return -EINVAL;
      
      	return 0;
      }
      
    • 2: Inclusion of icsk->icsk_ulp_ops->clone in the condition check

      static int inet_ulp_can_listen(const struct sock *sk)
      {
      	const struct inet_connection_sock *icsk = inet_csk(sk);
      
      	if (icsk->icsk_ulp_ops && !icsk->icsk_ulp_ops->clone)
      		return -EINVAL;
      
      	return 0;
      }
      
  • X: net/ipv4/tcp_ulp.c file modification

    • 1: No modification

    • 2: The EINVAL version

      err = -EINVAL;
      if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN)
      	goto out_err;
      
    • 3: The ENOTCONN version

      err = -ENOTCONN;
      if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN)
      	goto out_err;
      
  • Y: Initialization of err in inet_csk_listen_start

    • 1: De-initialization

      From

      int err = -EADDRINUSE;
      

      to

      int err;
      
    • 2: Left as-is: uninitialized

      int err;
      
    • 3: Left as-is: initialized to -EADDRINUSE

      int err = -EADDRINUSE;
      
  • Z: Restoration of err to -EADDRINUSE after checking inet_ulp_can_listen

    • 1: No

      err = inet_ulp_can_listen(sk);
      if (unlikely(err))
      	return err;
      
    • 2: Yes

      err = inet_ulp_can_listen(sk);
      if (unlikely(err))
      	return err;
      
      err = -EADDRINUSE;
      

The patch points are spaced as follows:

  W X Y Z
A 1 1 1 1
B 2 2 1 1
C 2 2 2 1
D 2 2 3 1
E 2 3 3 2
F 2 2 3 1

Discussion

  1. The W is somewhat correlated with X: upstream versions 4.14-5.4 simply lack the clone operation in ulp_ops, so it doesn't make sense to check for it and W1 goes with X1 for A - compare tcp_ulp_ops definition from 755193f (4.19) with the same struct in f8ed0a9 (5.10). What's interesting, however, is that Rocky LTS 8.6, despite being 4.18, does contain the clone ULP operation just like newer versions. (Perhaps it was backported by RedHat)

  2. Initialization of err (Y3) is pointless in any case where it occurs, as the variable is assigned inet_ulp_can_listen(sk) in the very next line. That was probably the reason for choosing Y1 in official backports for pre-6.0 versions (groups A, B).

  3. The restoration of err to -EADDRINUSE (axis Z), however, is not pointless in the pre-6.0 versions, including Rocky LTS 8.6 and LTS 8.8: a potential branching path exists which would result in the returned err different than before the change, for the exact same inputs. While effectively ignoring the initialization of err to -EADDRINUSE was justified in upstream mainline because of the inevitable assignment at line 1237, the same cannot be done in the earlier versions as the initial -EADDRINUSE can survive in err up to its return from the function.

  4. The use of -ENOTCONN instead of -EINVAL (X2 vs X1) is the result of applying commit 8ccc993, which was a follow-up of the mainline CVE fix 2c02d41:

    The referenced commit changed the error code returned by the kernel
    when preventing a non-established socket from attaching the ktls
    ULP. Before to such a commit, the user-space got ENOTCONN instead
    of EINVAL.
    
    The existing self-tests depend on such error code, and the change
    caused a failure:
    
      RUN           global.non_established ...
     tls.c:1673:non_established:Expected errno (22) == ENOTCONN (107)
     non_established: Test failed at step #3
              FAIL  global.non_established
    

Conclusions and solution

  1. The CVE-2023-0461 fix for LTS 8.6 should include the restoration of err to -EADDRINUSE (D: Z1Z2)just as it's done in LTS 8.8 (E: Z2). For the versions 9.2, 9.4 this is not required. Unless there is some reason going beyond the analysis local to the inet_csk_listen_start function this should probably be done to groups A, B as well, although that's outside of scope.
  2. The CVE-2023-0461 patch for LTS 8.6 should include the fix-of-the-fix 8ccc993, again, the same as it's done in LTS 8.8. The versions LTS 9.2, LTS 9.4 should be changed as well.
  3. The inclusion of clone operation checking is done properly in LTS 8.6 and should be left as it is, despite its deviation from the upstream backports.
  4. The initialization of err to -EADDRINUSE in all Rocky versions 8.6-9.4 can be left as is - it's harmless.

kABI check: passed

$ DEBUG=1 CVE=CVE-2023-0461 ./ninja.sh _kabi_checked__$(uname -m)--test--ciqlts8_6-CVE-2023-0461

[1/2] Check ABI of kernel [ciqlts8_6-CVE-2023-0461]
++ uname -m
+ python3 /data/src/ctrliq-github/kernel-dist-git-el-8.6/SOURCES/check-kabi -k /data/src/ctrliq-github/kernel-dist-git-el-8.6/SOURCES/Module.kabi_x86_64 -s vms/x86_64--build--ciqlts8_6/build_files/kernel-src-tree-ciqlts8_6-CVE-2023-0461/Module.symvers
kABI check passed
+ touch state/kernels/ciqlts8_6-CVE-2023-0461/x86_64/kabi_checked

Boot test: passed

boot-test.log

Kselftests: passed relative

Coverage

android, bpf (except test_xsk.sh, test_sockmap, test_progs-no_alu32, test_kmod.sh, test_progs), breakpoints, capabilities, core, cpu-hotplug, cpufreq, efivarfs, exec, firmware, fpu, ftrace, futex, gpio, intel_pstate, ipc, kcmp, kexec, kvm, lib, livepatch, membarrier, memfd, memory-hotplug, mount, net/forwarding (except mirror_gre_vlan_bridge_1q.sh, tc_actions.sh, sch_tbf_prio.sh, sch_tbf_ets.sh, sch_tbf_root.sh, ipip_hier_gre_keys.sh, mirror_gre_bridge_1d_vlan.sh, sch_ets.sh), net/mptcp (except simult_flows.sh), net (except udpgro_fwd.sh, ip_defrag.sh, txtimestamp.sh, udpgso_bench.sh, xfrm_policy.sh, reuseport_addr_any.sh, gro.sh, reuseaddr_conflict), netfilter (except nft_trans_stress.sh), nsfs, proc, pstore, ptrace, rseq, sgx, sigaltstack, size, splice, static_keys, tc-testing, timens, timers (except raw_skew), tpm2, vm, x86, zram

Reference

kselftests–ciqlts8_6–run1.log
kselftests–ciqlts8_6–run2.log
kselftests–ciqlts8_6–run3.log
kselftests–ciqlts8_6–run4.log

Patch

kselftests–ciqlts8_6-CVE-2023-0461–run1.log
kselftests–ciqlts8_6-CVE-2023-0461–run2.log
kselftests–ciqlts8_6-CVE-2023-0461–run3.log

Comparison

$ ktests.xsh diff -d kselftests*.log

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

TestCase  Status0  Status1  Status2  Status3  Status4  Status5  Status6  Summary
net:tls   fail     fail     fail     fail     pass     pass     pass     diff

The difference in test results showcase what was mentioned in the 8ccc993 commit - the net:tls test is now passing.

$ ktests.xsh show_groups kselftests*.log --test net:tls

kselftests--ciqlts8_6--run1.log:
kselftests--ciqlts8_6--run2.log:
kselftests--ciqlts8_6--run3.log:
kselftests--ciqlts8_6--run4.log:
net:tls:
…
# #  RUN           global.non_established ...
# # tls.c:1195:non_established:Expected errno (22) == ENOTCONN (107)
# # non_established: Test failed at step #2
# #          FAIL  global.non_established
…
# # FAILED: 90 / 91 tests passed.
# # Totals: pass:90 fail:1 xfail:0 xpass:0 skip:0 error:0
not ok 1 selftests: net: tls # exit=1


kselftests--ciqlts8_6-CVE-2023-0461--run1.log:
kselftests--ciqlts8_6-CVE-2023-0461--run2.log:
kselftests--ciqlts8_6-CVE-2023-0461--run3.log:
net:tls:
…
# #  RUN           global.non_established ...
# #            OK  global.non_established
# ok 1 global.non_established
…
# # PASSED: 91 / 91 tests passed.
# # Totals: pass:91 fail:0 xfail:0 xpass:0 skip:0 error:0
ok 1 selftests: net: tls

Specific tests: skipped

@pvts-mat
Copy link
Contributor Author

Just checked the net:tls results on ciqlts9_2 - the test is actually passing without the 8ccc993 patch

# # Starting 456 tests from 13 test cases.
# #  RUN           global.non_established ...
# #            OK  global.non_established

Perhaps the -EINVAL could be left there as it is in that case.

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.

🥌

@PlaidCat
Copy link
Collaborator

PlaidCat commented May 22, 2025

Thanks for the follow up of this and fixing the original backport.

Since both of these are technically bug fixes to the CVE could you put the cve tag to :

cve-bf CVE-2023-0461

This is more of a legacy tag information but we might be using it in the future.

jira VULN-3655
cve-bf CVE-2023-0461
commit-author Paolo Abeni <[email protected]>
commit 8ccc993
upstream-diff This commit is the closure of
              68e4adc, solving two
              issues:
              1. The backported mainline fix
                 2c02d41 had a
                 follow-up in
                 8ccc993, which was
                 missing from `ciqlts8_6'. (The original intent of
                 the cherry-picked commit)
              2. The way changes to `inet_csk_listen_start' were
                 applied from upstream left a potential branching
                 path which would result in the returned `err'
                 different than before the change, for the exact
                 same inputs. While effectively ignoring the
                 initialization of `err' to `-EADDRINUSE' was
                 justified in upstream because of the inevitable
                 assignment at line 1237, the same cannot be done in
                 the versions prior to 9.2 as the initial
                 `-EADDRINUSE' can survive in `err' up to its
                 returning from function. (The piggy-backed
                 correction included here for the lack of better
                 place)

The referenced commit changed the error code returned by the kernel
when preventing a non-established socket from attaching the ktls
ULP. Before to such a commit, the user-space got ENOTCONN instead
of EINVAL.

The existing self-tests depend on such error code, and the change
caused a failure:

  RUN           global.non_established ...
 tls.c:1673:non_established:Expected errno (22) == ENOTCONN (107)
 non_established: Test failed at step ctrliq#3
          FAIL  global.non_established

In the unlikely event existing applications do the same, address
the issue by restoring the prior error code in the above scenario.

Note that the only other ULP performing similar checks at init
time - smc_ulp_ops - also fails with ENOTCONN when trying to attach
the ULP to a non-established socket.

	Reported-by: Sabrina Dubroca <[email protected]>
Fixes: 2c02d41 ("net/ulp: prevent ULP without clone op from entering the LISTEN status")
	Signed-off-by: Paolo Abeni <[email protected]>
	Reviewed-by: Sabrina Dubroca <[email protected]>
Link: https://lore.kernel.org/r/7bb199e7a93317fb6f8bf8b9b2dc71c18f337cde.1674042685.git.pabeni@redhat.com
	Signed-off-by: Jakub Kicinski <[email protected]>
(cherry picked from commit 8ccc993)
	Signed-off-by: Marcin Wcisło <[email protected]>
@pvts-mat pvts-mat force-pushed the ciqlts8_6-CVE-2023-0461 branch from 9c615be to 90a11ca Compare May 22, 2025 20:34
Copy link
Collaborator

@PlaidCat PlaidCat left a comment

Choose a reason for hiding this comment

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

Thank You

:shipit:

@PlaidCat PlaidCat merged commit fc9306d into ctrliq:ciqlts8_6 May 23, 2025
2 checks 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