Skip to content

[LTS 8.6] cifs: CVE-2023-53751#1059

Open
pvts-mat wants to merge 4 commits intoctrliq:ciqlts8_6from
pvts-mat:ciqlts8_6-CVE-2023-53751
Open

[LTS 8.6] cifs: CVE-2023-53751#1059
pvts-mat wants to merge 4 commits intoctrliq:ciqlts8_6from
pvts-mat:ciqlts8_6-CVE-2023-53751

Conversation

@pvts-mat
Copy link
Copy Markdown
Contributor

@pvts-mat pvts-mat commented Apr 1, 2026

[LTS 8.6]

CVE-2023-53751 VULN-169227

Background

From #431:

CIFS (Common Internet File System) is basically a different name for Microsoft's SMB (Server Message Block) protocol, allowing for sharing files and printers on the network. Functionality-wise CIFS is the same as more Linux-native NFS system. From the practical perspective, using the CIFS module boils down to mounting appropriately addressed remote "share" at a local directory.

Commits

cifs: fix potential deadlock in direct reclaim

jira VULN-169227
cve-pre CVE-2023-53751
commit-author Vincent Whitchurch <vincent.whitchurch@axis.com>
commit cc391b694ff085f62f133e6b8f864d43a8e69dfd
upstream-diff |
  Multiple differences across many files due to large discrepancies
  between upstream and LTS 8.6, HOWEVER, the commit was obtained in the
  exact same way as on upstream, namely:
  1. locating all usages of `TCP_Server_Info::srv_mutex',
  2. changing all `mutex_lock(&<server_ptr>->srv_mutex)' to
     `cifs_server_lock(<server_ptr>)',
  3. changing all `mutex_unlock(&<server_ptr>->srv_mutex)' to
     `cifs_server_unlock(<server_ptr>)',
  4. renaming `srv_mutex~' to `_srv_mutex' to make sure nothing was
     missed.
  In this regard the commit doesn't differ from the upstream.
cifs: fix race in assemble_neg_contexts()

jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@cjr.nz>
commit 775e44d6d86dca400d614cbda5dab4def4951fe7
upstream-diff Ignored the introduction of `pserver' variable, as well as
  the usage of `hostname' local, as they were only needed in the upstream
  because of the dual source of the server hostname, introduced in the
  non-backported commit 9de74996a739bf0b7b5d8c260bd207ad6007442b ("smb3:
  use netname when available on secondary channels")
cifs: protect access of TCP_Server_Info::{dstaddr,hostname}

jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@cjr.nz>
commit 39a154fc2d172a3a5865e5a9fa2a2983eb7a99ac
upstream-diff Manually obtained commit. Upstream fixes appear in
  multiple places, but they all cover only the `cifs_tree_connect()'
  function (the CONFIG_CIFS_DFS_UPCALL=y variant) and its call tree. Same
  goes for the backported version, taking into account the lack of a major
  function rewrite in c88f7dcd6d6429197fc2fd87b54a894ffcd48e8e ("cifs:
  support nested dfs links over reconnect").
  Upstream call tree at the moment of the fix and the changes at each
  level:
    cifs_tree_connect()
          Put `cifs_server_{lock/unlock}(server)' around `scnprintf(...,
          server->hostname);' call.
    tree_connect_dfs_target()
          No changes.
    __tree_connect_dfs_target()
          Removed `extract_unc_hostname(server->hostname, ...)' call, used
          full `server->hostname' instead of its substring `tcp_host'.
    target_share_matches_server()
          Put `cifs_server_{lock/unlock}(server)' around
          `server->hostname' accesses in conditional expression and debug
          message composition before the `match_target_ip()' call.
    match_target_ip()
          Put `spin_{lock/unlock}(&server->srv_lock)' around
          `cifs_match_ipaddr(...server->dstaddr, ....)' call.
  Backported version call tree:
    cifs_tree_connect()
          - Put `cifs_server_{lock/unlock}(server)' around `scnprintf(...,
            server->hostname)' call - like in the upstream.
          - Put `cifs_server_{lock/unlock}(server)' around
            `server->hostname' (and its substring `tcp_host') access in
            conditional expression and debug message composition before
            the `match_target_ip()' call - like in the upstream. This
            required moving the `extract_unc_hostname()' call inside the
            loop. Unlike in the upstream it was not removed entirely,
            opting for functional equivalence between the patched and
            non-patched version instead of strictly preserving backported
            commit's changes; perhaps at the time of the upstream fix the
            equality `server->hostname' = `tcp_host' held true, allowing
            for the reduction of `extract_unc_hostname()' call, but that
            was not certain for the ciqlts8_6 version. If it wasn't true
            then the commit mixed a bugfix with a functional change which
            was simply filtered out here. Moving `extract_unc_hostname()'
            call inside the loop did not create any algorithmic
            inconsistencies which weren't already present (if any), merely
            preventing them from ending in bad memory access. In the
            upstream the value of `server->hostname' isn't saved to remain
            constant for all loop iterations in the
            `__tree_connect_dfs_target()' function either. A ngeligible
            performance penalty associated with the calculation redundancy
            was accepted given the simplicity of the fix it allowed.
    match_target_ip()
          Put `spin_{lock/unlock}(&cifs_tcp_ses_lock)' around the
          `cifs_match_ipaddr(...server->dstaddr, ...)' call - like in the
          upstream, except for the `cifs_tcp_ses_lock' instead of
          `server->srv_lock'. The latter was introduced in the
          non-backported commit d7d7a66aacd6fd8ca57baf08a7bac5421282f6f8
          ("cifs: avoid use of global locks for high contention
          data"). The same pattern of protecting `server->dstaddr' with
          `cifs_tcp_ses_lock' can be observed in ciqlts8_6 in the
          `reconn_set_ipaddr_from_hostname()' function.
cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname

jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@manguebit.com>
commit 90c49fce1c43e1cc152695e20363ff5087897c09
upstream-diff |
  fs/cifs/cifsglob.h
        Added `srv_lock' to the `TCP_Server_Info' struct as it was done in
        the non-backported commit d7d7a66aacd6fd8ca57baf08a7bac5421282f6f8
        ("cifs: avoid use of global locks for high contention data").
  fs/cifs/cifs_debug.c
        Ignored changes in `cifs_debug_data_proc_show()' because the code
        using `hostname' was only added in the non-backported commit
        40f077a02bf9d70719128d2a807e28a3503711eb ("cifs: clarify hostname
        vs ip address in /proc/fs/cifs/DebugData").
  fs/cifs/connect.c
        - Changed the `reconn_set_next_dfs_target()' function instead of
          `__reconnect_target_unlocked()' which doesn't exist in LTS
          8.6. The `__reconnect_target_unlocked()' is where the `hostname'
          is "updated once or many times during reconnect" (see original
          commit message). The "reconnect" refers to the function
          `cifs_reconnect()', which calls `__reconnect_target_unlocked()'
          on a third level down the call tree.  Relative to LTS 8.6 the
          `cifs_reconnect()' underwent major rewrites in the commits
          bbcce368044572d0802c3bbb8ef3fe98f581d803 ("cifs: split out dfs
          code from cifs_reconnect()") and
          c88f7dcd6d6429197fc2fd87b54a894ffcd48e8e ("cifs: support nested
          dfs links over reconnect"). In LTS 8.6 the updating of
          `hostname' can be tracked down to the
          `reconn_set_next_dfs_target()' function called by
          `cifs_reconnect()'.
        - Added initialization of `TCP_Server_Info::srv_lock' in
          `cifs_get_tcp_session()' as it's done in d7d7a66aa.
        - In `match_server()' changed the lockdep assertion from holding
          `server->srv_lock' to holding `cifs_tcp_ses_lock', because this
          is the actual invariant for the LTS 8.6 version.
        - In `match_server()' sandwiched the `strcasecmp(server->hostname,
          ...)' call in the lock/unlock pair of `server->srv_lock',
          because in LTS 8.6 the `match_server()' is not called with the
          `server->srv_lock' held and yet this is the lock chosen to
          protect the `server->hostname' field.
  fs/cifs/sess.c
        Ignored changes in `cifs_try_adding_channels()' because the code
        using `hostname' was only added in the non-backported commit
        9c2dc11df50d1c8537075ff6b98472198e24438e ("smb3: do not attempt
        multichannel to server which does not support it"). As a result no
        changes to the file have been made.

Discussion

Overview

The official CVE-2023-53751 fix is just the last commit cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname. However, the discrepancies between the upstream at the moment of that fix and LTS 8.6 were too large for cherry-picking to make any sense and the changes had to be done manually. This, in turn, prompted tracking of all TCP_Server_Info::hostname usages, as locking problems require global awareness of the synchronized data usage to be done correctly. This led to the discovery of two other fixes addressing the exact same issue, which is a possible usage of hostname after it was freed during connection re-establishment:

  • cifs: fix race in assemble_neg_contexts(),
  • cifs: protect access of TCP_Server_Info::{dstaddr,hostname}.

As such they were included as part of CVE-2023-53751 fix too. The three commits are preceded by cifs: fix potential deadlock in direct reclaim serving as prerequisite. Coincidentally it also fixes a synchronization issue, although not related to CVE-2023-53751.

Threads

While the exact threading layout in cifs module was not established, a key piece of information can be found in commit dca6581:

* ONLY cifsd thread should call cifs_reconnect. For any other
* thread, use this function

The cifs_reconnect() function is a key player in CVE-2023-53751 issue, modifying the TCP_Server_Info::hostname field while other threads read it. While it wasn't backported to LTS 8.6 it references a "last patch"

The last patch attempted to use the same helper function for
both types of threads, but that causes other issues
with lock dependencies.

most likely the Fixes commit 2a05137, which wasn't backported either. This suggests that the cifs_reconnect() being used only by a single "cifsd" thread applies to LTS 8.6 as well. This explains why some server->hostname readings on the references list remain (and should remain) unguarded - they all hang on the cifs_reconnect() call tree.

Commits discussion

The following sections aim to complement the upstream-diff info, providing additional context, explanations, analysis, rationale, alternative solutions, etc.

The entirety of synchronization analysis carried out was based on the assumption that all the corresponding locking / unlocking pairs can be found in the same function. It would be infeasible otherwise. Fortunately the cifs module seems to be following that convention.

cifs: fix potential deadlock in direct reclaim

The change seems extensive but it's conceptually simple: instead of using raw mutex_lock() / mutex_unlock() pairs use cifs_server_lock() / cifs_server_unlock() functions, which are newly introduced wrappers around mutex_lock() / mutex_unlock(). What they add is starting / ending the GFP_NOFS allocation scope:

* GFP_NOFS will use direct reclaim but will not use any filesystem interfaces.

Without it a memory allocation within the mutex span cuold trigger direct reclaim and page writeback to a remote CIFS volume, which in turn would try to acquire the same mutex lock that is already held and cause a deadlock, see https://lore.kernel.org/all/20220523123755.GA13668@axis.com/:

The crypto shash allocation does allocations with GFP_KERNEL (i.e.,
GFP_NOFS is not set and so fs reclaim can be triggered) and this is
called under the CIFS srv_mutex. However, the CIFS srv_mutex is also
used in the reclaim path as the splat shows. This is the dependency
which lockdep is complaining about.

The cifs_server_lock() / cifs_server_unlock() functions are used by other commits in the proposed solution. Backporting cifs: fix potential deadlock in direct reclaim helps to avoid the dillemma of either introducing new, potentially deadlocking code paths, or juggling the nofs flag ad hoc for each place the mutex is acquired / freed.

cifs: fix race in assemble_neg_contexts()

The LTS 8.6 codebase allowed for considerable simplification of the change, explained sufficiently in the upstream-diff. Apart from that three questions deserve to be addressed.

  1. Is the writing of server->hostname protected by the server->srv_mutex in LTS 8.6?

    It is. The main area of concern for writing TCP_Server_Info::hostname is reconn_set_next_dfs_target(). It's called from cifs_reconnect() (and only there) at

    reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);

    This call is surrounded by

    mutex_lock(&server->srv_mutex);

    and one of

    mutex_unlock(&server->srv_mutex);

    mutex_unlock(&server->srv_mutex);

    Writing done by cifs_put_tcp_session() is not protected by the mutex, but neither it is on upstream. This function most likely marks the end of any TCP_Server_Info object usage.

  2. Is the LTS 8.6 version of assemble_neg_contexts() always called outside of server->srv_mutex lock?

    It is. The only assemble_neg_contexts() call can be found in the SMB2_negotiate() function at

    assemble_neg_contexts(req, server, &total_len);

    The SMB2_negotiate(), in turn, is only called in smb2_negotiate() at

    rc = SMB2_negotiate(xid, ses);

    No srv_mutex locking to be found in either of these functions. The smb2_negotiate() itself is an external operation

    .negotiate = smb2_negotiate,

    .negotiate = smb2_negotiate,

    .negotiate = smb2_negotiate,

    .negotiate = smb2_negotiate,

    so unlikely to be called with the cifs-specific mutex held.

  3. Does the LTS 8.6 version of build_netname_ctxt() never lock the server->srv_mutex lock?

    It doesn't lock server->srv_mutex.

    The call tree:

    • build_netname_ctxt() (fs/nls/nls_base.c): no locks
      • load_nls_default() (fs/nls/nls_base.c): no locks
        • load_nls() (fs/nls/nls_base.c): no locks
          • find_nls() (fs/nls/nls_base.c): no locks
            • try_module_get() (kernel/module.c) function outside of cifs module, no cifs locks expected to be used.
          • try_then_request_module() (include/linux/kmod.h): function outside of cifs module, no cifs locks expected to be used.
      • cifs_strtoUTF16() (fs/cifs/cifs_unicode.c): string-related utility, unlikely to use any locks.
      • cpu_to_le16() (include/linux/byteorder/generic.h) big endian / little endian calculations outside of cifs.

cifs: protect access of TCP_Server_Info::{dstaddr,hostname}

The upstream fix addresses the reads of hostname in the cifs_tree_connect() function and its subroutine target_share_matches_server(). The latter is basically a portion of code extracted from cifs_tree_connect() in the non-backported commit c88f7dc. That commit involves more changes to cifs_tree_connect(), transforming it from the LTS 8.6 call tree:

  • cifs_tree_connect() (fs/cifs/connect.c)
    • match_target_ip() (fs/cifs/misc.c)

into:

  • cifs_tree_connect() (fs/cifs/connect.c)
    • tree_connect_dfs_target() (fs/cifs/dfs.c)
      • __tree_connect_dfs_target() (fs/cifs/dfs.c)
        • target_share_matches_server() (fs/cifs/dfs.c)
          • match_target_ip() (fs/cifs/misc.c)

(with functions narrowed down to those relevant to the fix).

The body of target_share_matches_server() from before the 39a154f fix

extract_unc_hostname(share, &dfs_host, &dfs_host_len);
/* Check if hostnames or addresses match */
if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
dfs_host, (int)tcp_host_len, tcp_host);
rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
if (rc)
cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
}

corresponds to this LTS 8.6 fragment of the cifs_tree_connect() function:

extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
dfs_host, (int)tcp_host_len, tcp_host);
rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
if (rc) {
cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
break;
}

The modified fragment of the __tree_connect_dfs_target() function, in turn

extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
tit = dfs_cache_get_tgt_iterator(tl);
if (!tit) {
rc = -ENOENT;
goto out;
}
/* Try to tree connect to all dfs targets */
for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
const char *target = dfs_cache_get_tgt_name(tit);
struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
kfree(share);
kfree(prefix);
share = prefix = NULL;
/* Check if share matches with tcp ses */
rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
if (rc) {
cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
break;
}
rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
&target_match);
if (rc)
break;
if (!target_match) {
rc = -EHOSTUNREACH;
continue;
}

corresponds roughly to this LTS 8.6 fragment of cifs_tree_connect() (encapsulating the previous one):

extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
bool target_match;
kfree(share);
kfree(prefix);
share = NULL;
prefix = NULL;
rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
if (rc) {
cifs_dbg(VFS, "%s: failed to parse target share %d\n",
__func__, rc);
continue;
}
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
dfs_host, (int)tcp_host_len, tcp_host);
rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
if (rc) {
cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
break;
}
if (!target_match) {
cifs_dbg(FYI, "%s: skipping target\n", __func__);
continue;
}
}

The upstream fix does not merely wrap the hostname usage with the TCP_Server_Info::srv_mutex lock, but also changes the functional semantics by comparing dfs_host with server->hostname directly instead of tcp_host. This functional change was omitted in the backport.

From the memory access synchronization perspective it's important to remember that tcp_host shares memory with server->hostname (see definition of extract_unc_hostname()), so it's not enough to just protect this single line

extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);

but all code execution paths starting from the server->hostname read to the last usage of tcp_host. This is what motivated moving the extract_unc_hostname() call inside the loop in the backported version. The alternative solutions considered were to either

  1. allocate a local copy of server->hostname, or
  2. guard the whole loop.

The first option was rejected as deviating too much from the upstream fix, at least compared to the eventually proposed solution. There was also no such code pattern found in the cifs module anywhere. The second option was rejected as deviating too much from the upstream as well - there might have been a reason in the upstream to lock the hostname access on a per-iteration basis.

Apart from protecting TCP_Server_Info::hostname the upstream fix also puts protection over TCP_Server_Info::dstaddr. While it's not part of the CVE-2023-53751 issue, the change was included for the sake of backport fidelity to the upstream. The TCP_Server_Info::srv_lock could not have been used, as it was introduced in the non-backported commit d7d7a66 cifs: avoid use of global locks for high contention data. (This lock was eventually included in the next commit cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname, but for a very specific function and could not work in this case.) The usage of cifs_tcp_ses_lock was motivated by the existence of this, ciqlts8_6-native, very similar code snippet:

spin_lock(&cifs_tcp_ses_lock);
rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr,
strlen(ipaddr));
spin_unlock(&cifs_tcp_ses_lock);

cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname

The main problem with backporting this commit was that it was based on using TCP_Server_Info::srv_lock lock not present in LTS 8.6. It was introduced in a large commit d7d7a66 cifs: avoid use of global locks for high contention data rewriting most of the spin locks used in the cifs module. Backporting it as prerequisite was not an option, as it is too extensive, too conflicting with LTS 8.6 codebase and dealing with too delicate matter.

In that commit, among other changes, most of the usages of cifs_tcp_ses_lock were offloaded to the newly introduced cifs_tcon::tc_lock, cifs_ses::ses_lock and TCP_Server_Info::srv_lock. At the same time the TCP_Server_Info::srv_lock was not used in place of any lock other than cifs_tcp_ses_lock (in particular not the GlobalMid_Lock - another de-loaded lock in that commit). As such the TCP_Server_Info::srv_lock became a "sub-lock" of the more general cifs_tcp_ses_lock used before, and in LTS 8.6. Naturally, using cifs_tcp_ses_lock instead of srv_lock was considered when backporting 90c49fc.

Unfortunately the backported commit locks srv_lock in the cifs_server_dbg_func() macro, which - through the cifs_server_dbg() wrapper - is used quite extensively in the cifs module (78 references of cifs_server_dbg() found). Changing srv_lock to cifs_tcp_ses_lock required tracking all callers of the macro (and their callers, and so on) for whether they don't call it with the cifs_tcp_ses_lock locked already, or a deadlock would occur. As it turned out during the process there was at least one function which required changing, see

static
int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{
struct cifs_chan *chan;
struct cifs_ses *ses = NULL;
struct TCP_Server_Info *it = NULL;
int i;
int rc = 0;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(it, &cifs_tcp_ses_list, tcp_ses_list) {
list_for_each_entry(ses, &it->smb_ses_list, smb_ses_list) {
if (ses->Suid == ses_id)
goto found;
}
}
cifs_server_dbg(VFS, "%s: Could not find session 0x%llx\n",
__func__, ses_id);
rc = -ENOENT;
goto out;
found:
if (ses->binding) {
/*
* If we are in the process of binding a new channel
* to an existing session, use the master connection
* session key
*/
memcpy(key, ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
goto out;
}
/*
* Otherwise, use the channel key.
*/
for (i = 0; i < ses->chan_count; i++) {
chan = ses->chans + i;
if (chan->server == server) {
memcpy(key, chan->signkey, SMB3_SIGN_KEY_SIZE);
goto out;
}
}
cifs_dbg(VFS,
"%s: Could not find channel signing key for session 0x%llx\n",
__func__, ses_id);
rc = -ENOENT;
out:
spin_unlock(&cifs_tcp_ses_lock);
return rc;
}

Given the extent of the work required, and the proven necessity of it, the attempt at using cifs_tcp_ses_lock instead of srv_lock in the backport was abandoned.

Instead the TCP_Server_Info::srv_lock was introduced as in d7d7a66, with its usages reduced to the extent of the 90c49fc backport only. This was perfectly enough for the CVE-2023-53751 fix to serve its purpose, as the commit was self-contained, in the sense, that:

  • no spinlock writing protection of TCP_Server_Info::hostname existed in LTS 8.6 before the fix, and
  • no spinlock reading protection of TCP_Server_Info::hostname existed in LTS 8.6 before the fix.

If that was not the case the other protection places would have to be aligned with the backported commit to also use the newly introduced TCP_Server_Info::srv_lock. (There were, of course, hostname writing protections in place already, upon which cifs: protect access of TCP_Server_Info::{dstaddr,hostname} and cifs: fix race in assemble_neg_contexts() backports were built, but they were mutex-based.)

Since TCP_Server_Info::srv_lock did not exist in LTS 8.6 before the backport it was not necessary to scan all the reversed call trees of the modified functions / macros.

Note that there is a bug in

server->hostname = extract_hostname(name);

stemming, most likely, from the assumption that extract_hostname() returns NULL upon error, which is false. It was partially addressed in the (backported) commit 8428817, but the issue of hostname-reading threads receiving rubbish if extract_hostname() failed remains. Fixing it was deemed outside of CVE-2023-53751 fix scope though.

All TCP_Server_Info::hostname usages in LTS 8.6

Reference list with a comment about the protection situation for each.

reconn_set_ipaddr_from_hostname()

if (!server->hostname)

len = strlen(server->hostname) + 3;

scnprintf(unc, len, "\\\\%s", server->hostname);

cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
__func__, server->hostname, rc);

Function called in two places, both within the cifs_reconnect() call tree - no need for reading protection.

rc = reconn_set_ipaddr_from_hostname(server);

rc = reconn_set_ipaddr_from_hostname(server);

reconn_set_next_dfs_target()

kfree(server->hostname);
server->hostname = extract_hostname(name);

Writing protection addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname.

cifs_reconnect()

trace_smb3_reconnect(server->CurrentMid, server->hostname);

Usage in the cifs_reconnect() function - no need for reading protection.

cifs_echo_request()

cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
server->hostname);

Issue addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname (got rid of hostname read entirely)

match_server()

if (strcasecmp(server->hostname, ctx->server_hostname))

Issue addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname.

cifs_put_tcp_session()

kfree(server->hostname);
server->hostname = NULL;

There is a separate CVE-2025-21673 for that and the upstream fix fa2f990. Did not investigate further, can include it in the PR on request.

cifs_setup_ipc()

scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);

Issue addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname.

cifs_tree_connect()

scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);

extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);

if (dfs_host_len != tcp_host_len
|| strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
dfs_host, (int)tcp_host_len, tcp_host);

(memory is shared between server->hostname and tcp_host)

Issue addressed in cifs: protect access of TCP_Server_Info::{dstaddr,hostname}.

cifs_stats_proc_show()

seq_printf(m, " %d slow responses from %s for command %d\n",
atomic_read(&server->smb2slowcmd[j]),
server->hostname, j);

Issue addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname.

assemble_neg_contexts()

ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
server->hostname);

Issue addressed in cifs: fix race in assemble_neg_contexts().

cifs_get_spnego_key()

const char *hostname = server->hostname;

desc_len = MAX_VER_STR_LEN +
HOST_KEY_LEN + strlen(hostname) +
IP_KEY_LEN + INET6_ADDRSTRLEN +
MAX_MECH_STR_LEN +
UID_KEY_LEN + (sizeof(uid_t) * 2) +
CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;

sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
hostname);

Not protected even in the most recent upstream codebase - perhaps not needed.

struct key *
cifs_get_spnego_key(struct cifs_ses *sesInfo,
struct TCP_Server_Info *server)
{
struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
char *description, *dp;
size_t desc_len;
struct key *spnego_key;
const char *hostname = server->hostname;
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
host=hostname sec=mechanism uid=0xFF user=username */
desc_len = MAX_VER_STR_LEN +
HOST_KEY_LEN + strlen(hostname) +
IP_KEY_LEN + INET6_ADDRSTRLEN +
MAX_MECH_STR_LEN +
UID_KEY_LEN + (sizeof(uid_t) * 2) +
CREDUID_KEY_LEN + (sizeof(uid_t) * 2) +
PID_KEY_LEN + (sizeof(pid_t) * 2) + 1;
if (sesInfo->user_name)
desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
if (sesInfo->upcall_target == UPTARGET_MOUNT)
desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount")
else
desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app")
spnego_key = ERR_PTR(-ENOMEM);
description = kzalloc(desc_len, GFP_KERNEL);
if (description == NULL)
goto out;
dp = description;
/* start with version and hostname portion of UNC string */
spnego_key = ERR_PTR(-EINVAL);
dp += sprintf(dp, "ver=0x%x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION,
hostname);
/* add the server address */
if (server->dstaddr.ss_family == AF_INET)
dp += sprintf(dp, "ip4=%pI4", &sa->sin_addr);
else if (server->dstaddr.ss_family == AF_INET6)
dp += sprintf(dp, "ip6=%pI6", &sa6->sin6_addr);
else
goto out;
/* for now, only sec=krb5 and sec=mskrb5 and iakerb are valid */
if (server->sec_kerberos)
dp += sprintf(dp, ";sec=krb5");
else if (server->sec_mskerberos)
dp += sprintf(dp, ";sec=mskrb5");
else if (server->sec_iakerb)
dp += sprintf(dp, ";sec=iakerb");
else {
cifs_dbg(VFS, "unknown or missing server auth type, use krb5\n");
dp += sprintf(dp, ";sec=krb5");
}
dp += sprintf(dp, ";uid=0x%x",
from_kuid_munged(&init_user_ns, sesInfo->linux_uid));
dp += sprintf(dp, ";creduid=0x%x",
from_kuid_munged(&init_user_ns, sesInfo->cred_uid));
if (sesInfo->user_name)
dp += sprintf(dp, ";user=%s", sesInfo->user_name);
dp += sprintf(dp, ";pid=0x%x", current->pid);
if (sesInfo->upcall_target == UPTARGET_MOUNT)
dp += sprintf(dp, ";upcall_target=mount");
else
dp += sprintf(dp, ";upcall_target=app");
cifs_dbg(FYI, "key description = %s\n", description);
scoped_with_creds(spnego_cred)
spnego_key = request_key(&cifs_spnego_key_type, description, "");
trace_smb3_kerberos_auth(server, sesInfo, PTR_ERR_OR_ZERO(spnego_key));
#ifdef CONFIG_CIFS_DEBUG2
if (cifsFYI && !IS_ERR(spnego_key)) {
struct cifs_spnego_msg *msg = spnego_key->payload.data[0];
cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U,
msg->secblob_len + msg->sesskey_len));
}
#endif /* CONFIG_CIFS_DEBUG2 */
out:
kfree(description);
return spnego_key;
}

cifs_server_dbg_func()

const char *sn = ""; \
if (server && server->hostname) \
sn = server->hostname; \
if ((type) & FYI && cifsFYI & CIFS_INFO) { \
pr_debug_ ## ratefunc("%s: \\\\%s " fmt, \
__FILE__, sn, ##__VA_ARGS__); \
} else if ((type) & VFS) { \
pr_err_ ## ratefunc("VFS: \\\\%s " fmt, \
sn, ##__VA_ARGS__); \
} else if ((type) & NOISY && (NOISY != 0)) { \
pr_debug_ ## ratefunc("\\\\%s " fmt, \
sn, ##__VA_ARGS__); \
} \

Issue addressed in cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname.

cifs_server_dbg()

pr_debug("\\\\%s " fmt, \
server->hostname, ##__VA_ARGS__); \

Unreachable code, no need for protection (why even there?).

__smb_send_rqst()

trace_smb3_partial_send_reconnect(server->CurrentMid,
server->hostname);

Already protected, although higher up the callers tree:

  1. __smb_send_rqst() no protection
    1. smb_send_rqst() (fs/cifs/transport.c) no protection
      1. compound_send_recv() (fs/cifs/transport.c) protected by server->srv_mutex
      2. cifs_call_async() (fs/cifs/transport.c) protected by server->srv_mutex
    2. smb_send() (fs/cifs/transport.c) no protection
      1. ip_rfc1001_connect() (fs/cifs/connect.c) no protection
        1. generic_ip_connect() (fs/cifs/connect.c) no protection
          1. cifs_reconnect() (fs/cifs/connect.c) protected by server->srv_mutex
          2. ip_connect() (fs/cifs/connect.c) no protection
            1. cifs_get_tcp_session() (fs/cifs/connect.c) no protection, but in this context it's not needed, see
      2. send_nt_cancel() (fs/cifs/smb1ops.c) protected by server->srv_mutex
      3. SendReceive() (fs/cifs/transport.c) protected by server->srv_mutex
      4. SendReceiveBlockingLock() (fs/cifs/transport.c) protected by server->srv_mutex

wait_for_free_credits()

trace_smb3_credit_timeout(server->CurrentMid,
server->hostname, num_credits, 0);

trace_smb3_credit_timeout(
server->CurrentMid,
server->hostname, num_credits,
0);

This hostname usage doesn't seem to be protected, neither in wait_for_free_credits() nor in any place up the callers tree:

  1. wait_for_free_credits() (fs/cifs/transport.c) no protection
    1. wait_for_compound_request() (fs/cifs/transport.c) no protection
      1. compound_send_recv() (fs/cifs/transport.c) no protection and unlikely to have any up the callers tree, because srv_mutex mutex used already
    2. wait_for_free_request() (fs/cifs/transport.c) no protection
      1. cifs_call_async() (fs/cifs/transport.c) no protection and unlikely to have any up the callers tree, because srv_mutex mutex used already
      2. SendReceive() (fs/cifs/transport.c) no protection and unlikely to have any up the callers tree, because srv_mutex mutex used already
      3. SendReceiveBlockingLock() (fs/cifs/transport.c) no protection and unlikely to have any up the callers tree, because srv_mutex mutex used already

These usages aren't protected in the upstream either

static int
wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
const int timeout, const int flags,
unsigned int *instance)
{
long rc;
int *credits;
int optype;
long int t;
int scredits, in_flight;
if (timeout < 0)
t = MAX_JIFFY_OFFSET;
else
t = msecs_to_jiffies(timeout);
optype = flags & CIFS_OP_MASK;
*instance = 0;
credits = server->ops->get_credits_field(server, optype);
/* Since an echo is already inflight, no need to wait to send another */
if (*credits <= 0 && optype == CIFS_ECHO_OP)
return -EAGAIN;
spin_lock(&server->req_lock);
if ((flags & CIFS_TIMEOUT_MASK) == CIFS_NON_BLOCKING) {
/* oplock breaks must not be held up */
server->in_flight++;
if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
*credits -= 1;
*instance = server->reconnect_instance;
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_nblk_credits(server->current_mid,
server->conn_id, server->hostname, scredits, -1, in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, 1, scredits);
return 0;
}
while (1) {
spin_unlock(&server->req_lock);
spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);
spin_lock(&server->req_lock);
if (*credits < num_credits) {
scredits = *credits;
spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server);
rc = wait_event_killable_timeout(server->request_q,
has_credits(server, credits, num_credits), t);
cifs_num_waiters_dec(server);
if (!rc) {
spin_lock(&server->req_lock);
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(server->current_mid,
server->conn_id, server->hostname, scredits,
num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout);
return -EBUSY;
}
if (rc == -ERESTARTSYS)
return -ERESTARTSYS;
spin_lock(&server->req_lock);
} else {
/*
* For normal commands, reserve the last MAX_COMPOUND
* credits to compound requests.
* Otherwise these compounds could be permanently
* starved for credits by single-credit requests.
*
* To prevent spinning CPU, block this thread until
* there are >MAX_COMPOUND credits available.
* But only do this is we already have a lot of
* credits in flight to avoid triggering this check
* for servers that are slow to hand out credits on
* new sessions.
*/
if (!optype && num_credits == 1 &&
server->in_flight > 2 * MAX_COMPOUND &&
*credits <= MAX_COMPOUND) {
spin_unlock(&server->req_lock);
cifs_num_waiters_inc(server);
rc = wait_event_killable_timeout(
server->request_q,
has_credits(server, credits,
MAX_COMPOUND + 1),
t);
cifs_num_waiters_dec(server);
if (!rc) {
spin_lock(&server->req_lock);
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_credit_timeout(
server->current_mid,
server->conn_id, server->hostname,
scredits, num_credits, in_flight);
cifs_server_dbg(VFS, "wait timed out after %d ms\n",
timeout);
return -EBUSY;
}
if (rc == -ERESTARTSYS)
return -ERESTARTSYS;
spin_lock(&server->req_lock);
continue;
}
/*
* Can not count locking commands against total
* as they are allowed to block on server.
*/
/* update # of requests on the wire to server */
if ((flags & CIFS_TIMEOUT_MASK) != CIFS_BLOCKING_OP) {
*credits -= num_credits;
server->in_flight += num_credits;
if (server->in_flight > server->max_in_flight)
server->max_in_flight = server->in_flight;
*instance = server->reconnect_instance;
}
scredits = *credits;
in_flight = server->in_flight;
spin_unlock(&server->req_lock);
trace_smb3_waitff_credits(server->current_mid,
server->conn_id, server->hostname, scredits,
-(num_credits), in_flight);
cifs_dbg(FYI, "%s: remove %u credits total=%d\n",
__func__, num_credits, scredits);
break;
}
}
return 0;
}

This might be a bug, especially given that the sibling TCP_Server_Info field - tcpStatus - is being protected by srv_lock, eg.

spin_lock(&server->srv_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->srv_lock);
return -ENOENT;
}
spin_unlock(&server->srv_lock);

No need to outrun the upstream though. Not investigated further.

smb2_add_credits()

trace_smb3_reconnect_with_invalid_credits(server->CurrentMid,
server->hostname, *val, add);

trace_smb3_add_credits(server->CurrentMid,
server->hostname, rc, add);

See wait_for_free_credits().

cifs_get_tcp_session()

tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL);
if (!tcp_ses->hostname) {
rc = -ENOMEM;
goto out_err;
}

kfree(tcp_ses->hostname);

No protections in the most recent kernel-mainline either (TCP_Server_Info object creation subroutine, perhaps just no need for protection yet).

kABI check: passed

[0/1] kabi_check_kernel	Check ABI of kernel [ciqlts8_6-CVE-2023-53751]	_kabi_check_kernel__x86_64--test--ciqlts8_6-CVE-2023-53751
+ dist_git_version=el-8.6
+ local_version=ciqlts8_6-CVE-2023-53751
+ arch=x86_64
+ user=pvts
+ buildmachine=x86_64--build--ciqlts8_6
+ virsh_timeout=600
+ ssh_daemon_wait=20
+ src_dir=/mnt/code/kernel-dist-git-el-8.6
+ build_dir=/mnt/build_files/kernel-src-tree-ciqlts8_6-CVE-2023-53751
+ sudo chmod +x /data/src/ctrliq-github-haskell/kernel-dist-git-el-8.6/SOURCES/check-kabi
+ ninja-back/virssh.xsh --max 8 --shutdown-on-success --shutdown-on-failure --timeout 600 --ssh-daemon-wait 20 pvts x86_64--build--ciqlts8_6 ''\''/mnt/code/kernel-dist-git-el-8.6/SOURCES/check-kabi'\'' -k '\''/mnt/code/kernel-dist-git-el-8.6/SOURCES/Module.kabi_x86_64'\'' -s '\''/mnt/build_files/kernel-src-tree-ciqlts8_6-CVE-2023-53751/Module.symvers'\'''
kABI check passed
+ touch state/kernels/ciqlts8_6-CVE-2023-53751/x86_64/kabi_checked

Boot test: passed

boot-test.log

Kselftests: passed relative

Reference

kselftests–ciqlts8_6–run1.log

Patch

kselftests–ciqlts8_6-CVE-2023-53751–run1.log
kselftests–ciqlts8_6-CVE-2023-53751–run2.log

Comparison

The tests results for the reference and the patch are the same.

$ ktests.xsh diff  kselftests*.log

Column    File
--------  ----------------------------------------------
Status0   kselftests--ciqlts8_6--run1.log
Status1   kselftests--ciqlts8_6-CVE-2023-53751--run1.log
Status2   kselftests--ciqlts8_6-CVE-2023-53751--run2.log

TestCase                                     Status0  Status1  Status2  Summary
android:run.sh                               skip     skip     skip     same
bpf:get_cgroup_id_user                       pass     pass     pass     same
bpf:test_bpftool.sh                          pass     pass     pass     same
bpf:test_bpftool_build.sh                    pass     pass     pass     same
bpf:test_bpftool_metadata.sh                 pass     pass     pass     same
bpf:test_cgroup_storage                      pass     pass     pass     same
bpf:test_dev_cgroup                          pass     pass     pass     same
bpf:test_doc_build.sh                        pass     pass     pass     same
bpf:test_flow_dissector.sh                   pass     pass     pass     same
bpf:test_lirc_mode2.sh                       pass     pass     pass     same
bpf:test_lpm_map                             pass     pass     pass     same
bpf:test_lru_map                             pass     pass     pass     same
bpf:test_lwt_ip_encap.sh                     pass     pass     pass     same
bpf:test_lwt_seg6local.sh                    pass     pass     pass     same
bpf:test_netcnt                              pass     pass     pass     same
bpf:test_offload.py                          fail     fail     fail     same
bpf:test_skb_cgroup_id.sh                    pass     pass     pass     same
bpf:test_sock                                pass     pass     pass     same
bpf:test_sock_addr.sh                        pass     pass     pass     same
bpf:test_sysctl                              pass     pass     pass     same
bpf:test_tag                                 pass     pass     pass     same
bpf:test_tc_edt.sh                           pass     pass     pass     same
bpf:test_tc_tunnel.sh                        pass     pass     pass     same
bpf:test_tcp_check_syncookie.sh              pass     pass     pass     same
bpf:test_tcpnotify_user                      pass     pass     pass     same
bpf:test_tunnel.sh                           pass     pass     pass     same
bpf:test_verifier                            pass     pass     pass     same
bpf:test_verifier_log                        pass     pass     pass     same
bpf:test_xdp_meta.sh                         pass     pass     pass     same
bpf:test_xdp_redirect.sh                     pass     pass     pass     same
bpf:test_xdp_veth.sh                         pass     pass     pass     same
bpf:test_xdp_vlan_mode_generic.sh            pass     pass     pass     same
bpf:test_xdp_vlan_mode_native.sh             pass     pass     pass     same
bpf:test_xdping.sh                           pass     pass     pass     same
bpf:urandom_read                             pass     pass     pass     same
breakpoints:breakpoint_test                  pass     pass     pass     same
capabilities:test_execve                     pass     pass     pass     same
core:close_range_test                        pass     pass     pass     same
cpu-hotplug:cpu-on-off-test.sh               pass     pass     pass     same
cpufreq:main.sh                              fail     fail     fail     same
exec:execveat                                pass     pass     pass     same
firmware:fw_run_tests.sh                     skip     skip     skip     same
fpu:run_test_fpu.sh                          skip     skip     skip     same
fpu:test_fpu                                 pass     pass     pass     same
ftrace:ftracetest                            fail     fail     fail     same
futex:run.sh                                 pass     pass     pass     same
gpio:gpio-mockup.sh                          fail     fail     fail     same
intel_pstate:run.sh                          pass     pass     pass     same
ipc:msgque                                   pass     pass     pass     same
kcmp:kcmp_test                               pass     pass     pass     same
kexec:test_kexec_file_load.sh                skip     skip     skip     same
kexec:test_kexec_load.sh                     skip     skip     skip     same
kvm:access_tracking_perf_test                fail     fail     fail     same
kvm:amx_test                                 fail     fail     fail     same
kvm:cr4_cpuid_sync_test                      fail     fail     fail     same
kvm:debug_regs                               fail     fail     fail     same
kvm:demand_paging_test                       pass     pass     pass     same
kvm:dirty_log_perf_test                      pass     pass     pass     same
kvm:dirty_log_test                           fail     fail     fail     same
kvm:emulator_error_test                      fail     fail     fail     same
kvm:evmcs_test                               fail     fail     fail     same
kvm:get_cpuid_test                           fail     fail     fail     same
kvm:get_msr_index_features                   fail     fail     fail     same
kvm:hardware_disable_test                    pass     pass     pass     same
kvm:hyperv_clock                             fail     fail     fail     same
kvm:hyperv_cpuid                             fail     fail     fail     same
kvm:hyperv_features                          fail     fail     fail     same
kvm:kvm_binary_stats_test                    pass     pass     pass     same
kvm:kvm_create_max_vcpus                     skip     skip     skip     same
kvm:kvm_page_table_test                      pass     pass     pass     same
kvm:kvm_pv_test                              fail     fail     fail     same
kvm:memslot_modification_stress_test         pass     pass     pass     same
kvm:memslot_perf_test                        fail     fail     fail     same
kvm:mmio_warning_test                        fail     fail     fail     same
kvm:mmu_role_test                            fail     fail     fail     same
kvm:platform_info_test                       fail     fail     fail     same
kvm:rseq_test                                fail     fail     fail     same
kvm:set_boot_cpu_id                          fail     fail     fail     same
kvm:set_memory_region_test                   pass     pass     pass     same
kvm:set_sregs_test                           fail     fail     fail     same
kvm:smm_test                                 fail     fail     fail     same
kvm:state_test                               fail     fail     fail     same
kvm:steal_time                               pass     pass     pass     same
kvm:svm_int_ctl_test                         fail     fail     fail     same
kvm:svm_vmcall_test                          fail     fail     fail     same
kvm:sync_regs_test                           fail     fail     fail     same
kvm:tsc_msrs_test                            fail     fail     fail     same
kvm:userspace_msr_exit_test                  fail     fail     fail     same
kvm:vmx_apic_access_test                     fail     fail     fail     same
kvm:vmx_close_while_nested_test              fail     fail     fail     same
kvm:vmx_dirty_log_test                       fail     fail     fail     same
kvm:vmx_nested_tsc_scaling_test              fail     fail     fail     same
kvm:vmx_pmu_msrs_test                        fail     fail     fail     same
kvm:vmx_preemption_timer_test                fail     fail     fail     same
kvm:vmx_set_nested_state_test                fail     fail     fail     same
kvm:vmx_tsc_adjust_test                      fail     fail     fail     same
kvm:xapic_ipi_test                           fail     fail     fail     same
kvm:xen_shinfo_test                          fail     fail     fail     same
kvm:xen_vmcall_test                          fail     fail     fail     same
kvm:xss_msr_test                             fail     fail     fail     same
lib:bitmap.sh                                skip     skip     skip     same
lib:prime_numbers.sh                         skip     skip     skip     same
lib:printf.sh                                skip     skip     skip     same
lib:scanf.sh                                 fail     fail     fail     same
livepatch:test-callbacks.sh                  pass     pass     pass     same
livepatch:test-ftrace.sh                     pass     pass     pass     same
livepatch:test-livepatch.sh                  pass     pass     pass     same
livepatch:test-shadow-vars.sh                pass     pass     pass     same
livepatch:test-state.sh                      pass     pass     pass     same
membarrier:membarrier_test_multi_thread      pass     pass     pass     same
membarrier:membarrier_test_single_thread     pass     pass     pass     same
memfd:memfd_test                             pass     pass     pass     same
memfd:run_fuse_test.sh                       fail     fail     fail     same
memfd:run_hugetlbfs_test.sh                  pass     pass     pass     same
memory-hotplug:mem-on-off-test.sh            pass     pass     pass     same
mount:run_tests.sh                           pass     pass     pass     same
net/forwarding:bridge_port_isolation.sh      pass     pass     pass     same
net/forwarding:bridge_sticky_fdb.sh          pass     pass     pass     same
net/forwarding:bridge_vlan_aware.sh          fail     fail     fail     same
net/forwarding:bridge_vlan_unaware.sh        pass     pass     pass     same
net/forwarding:ethtool.sh                    fail     fail     fail     same
net/forwarding:gre_multipath.sh              fail     fail     fail     same
net/forwarding:ip6_forward_instats_vrf.sh    fail     fail     fail     same
net/forwarding:ipip_flat_gre.sh              pass     pass     pass     same
net/forwarding:ipip_flat_gre_key.sh          pass     pass     pass     same
net/forwarding:ipip_flat_gre_keys.sh         pass     pass     pass     same
net/forwarding:ipip_hier_gre.sh              pass     pass     pass     same
net/forwarding:ipip_hier_gre_key.sh          pass     pass     pass     same
net/forwarding:loopback.sh                   skip     skip     skip     same
net/forwarding:mirror_gre.sh                 fail     fail     fail     same
net/forwarding:mirror_gre_bound.sh           pass     pass     pass     same
net/forwarding:mirror_gre_bridge_1d.sh       pass     pass     pass     same
net/forwarding:mirror_gre_bridge_1q.sh       pass     pass     pass     same
net/forwarding:mirror_gre_bridge_1q_lag.sh   pass     pass     pass     same
net/forwarding:mirror_gre_changes.sh         fail     fail     fail     same
net/forwarding:mirror_gre_flower.sh          fail     fail     fail     same
net/forwarding:mirror_gre_lag_lacp.sh        pass     pass     pass     same
net/forwarding:mirror_gre_neigh.sh           pass     pass     pass     same
net/forwarding:mirror_gre_nh.sh              pass     pass     pass     same
net/forwarding:mirror_gre_vlan.sh            pass     pass     pass     same
net/forwarding:mirror_vlan.sh                pass     pass     pass     same
net/forwarding:router.sh                     fail     fail     fail     same
net/forwarding:router_bridge.sh              pass     pass     pass     same
net/forwarding:router_bridge_vlan.sh         pass     pass     pass     same
net/forwarding:router_broadcast.sh           fail     fail     fail     same
net/forwarding:router_multicast.sh           fail     fail     fail     same
net/forwarding:router_multipath.sh           fail     fail     fail     same
net/forwarding:router_vid_1.sh               pass     pass     pass     same
net/forwarding:tc_chains.sh                  pass     pass     pass     same
net/forwarding:tc_flower.sh                  pass     pass     pass     same
net/forwarding:tc_flower_router.sh           pass     pass     pass     same
net/forwarding:tc_mpls_l2vpn.sh              pass     pass     pass     same
net/forwarding:tc_shblocks.sh                pass     pass     pass     same
net/forwarding:tc_vlan_modify.sh             pass     pass     pass     same
net/forwarding:vxlan_asymmetric.sh           pass     pass     pass     same
net/forwarding:vxlan_bridge_1d.sh            fail     fail     fail     same
net/forwarding:vxlan_bridge_1d_port_8472.sh  pass     pass     pass     same
net/forwarding:vxlan_bridge_1q.sh            fail     fail     fail     same
net/forwarding:vxlan_bridge_1q_port_8472.sh  pass     pass     pass     same
net/forwarding:vxlan_symmetric.sh            pass     pass     pass     same
net/mptcp:diag.sh                            pass     pass     pass     same
net/mptcp:mptcp_connect.sh                   pass     pass     pass     same
net/mptcp:mptcp_sockopt.sh                   pass     pass     pass     same
net/mptcp:pm_netlink.sh                      pass     pass     pass     same
net:bareudp.sh                               pass     pass     pass     same
net:devlink_port_split.py                    pass     pass     pass     same
net:drop_monitor_tests.sh                    skip     skip     skip     same
net:fcnal-test.sh                            pass     pass     pass     same
net:fib-onlink-tests.sh                      pass     pass     pass     same
net:fib_rule_tests.sh                        fail     fail     fail     same
net:fib_tests.sh                             pass     pass     pass     same
net:gre_gso.sh                               pass     pass     pass     same
net:icmp_redirect.sh                         pass     pass     pass     same
net:ip6_gre_headroom.sh                      pass     pass     pass     same
net:ipv6_flowlabel.sh                        pass     pass     pass     same
net:l2tp.sh                                  pass     pass     pass     same
net:msg_zerocopy.sh                          fail     fail     fail     same
net:netdevice.sh                             pass     pass     pass     same
net:pmtu.sh                                  pass     pass     pass     same
net:psock_snd.sh                             fail     fail     fail     same
net:reuseaddr_conflict                       pass     pass     pass     same
net:reuseport_bpf                            pass     pass     pass     same
net:reuseport_bpf_cpu                        pass     pass     pass     same
net:reuseport_bpf_numa                       pass     pass     pass     same
net:reuseport_dualstack                      pass     pass     pass     same
net:rtnetlink.sh                             skip     skip     skip     same
net:run_afpackettests                        pass     pass     pass     same
net:run_netsocktests                         pass     pass     pass     same
net:rxtimestamp.sh                           pass     pass     pass     same
net:so_txtime.sh                             fail     fail     fail     same
net:test_bpf.sh                              pass     pass     pass     same
net:test_vxlan_fdb_changelink.sh             pass     pass     pass     same
net:tls                                      pass     pass     pass     same
net:traceroute.sh                            pass     pass     pass     same
net:udpgro.sh                                fail     fail     fail     same
net:udpgro_bench.sh                          fail     fail     fail     same
net:udpgso.sh                                pass     pass     pass     same
net:veth.sh                                  fail     fail     fail     same
net:vrf-xfrm-tests.sh                        pass     pass     pass     same
netfilter:conntrack_icmp_related.sh          fail     fail     fail     same
netfilter:conntrack_tcp_unreplied.sh         fail     fail     fail     same
netfilter:ipvs.sh                            skip     skip     skip     same
netfilter:nft_flowtable.sh                   fail     fail     fail     same
netfilter:nft_meta.sh                        pass     pass     pass     same
netfilter:nft_nat.sh                         skip     skip     skip     same
netfilter:nft_queue.sh                       skip     skip     skip     same
nsfs:owner                                   pass     pass     pass     same
nsfs:pidns                                   pass     pass     pass     same
proc:fd-001-lookup                           pass     pass     pass     same
proc:fd-002-posix-eq                         pass     pass     pass     same
proc:fd-003-kthread                          pass     pass     pass     same
proc:proc-loadavg-001                        pass     pass     pass     same
proc:proc-self-map-files-001                 pass     pass     pass     same
proc:proc-self-map-files-002                 fail     fail     fail     same
proc:proc-self-syscall                       pass     pass     pass     same
proc:proc-self-wchan                         pass     pass     pass     same
proc:proc-uptime-001                         pass     pass     pass     same
proc:proc-uptime-002                         pass     pass     pass     same
proc:read                                    pass     pass     pass     same
proc:setns-dcache                            fail     fail     fail     same
pstore:pstore_post_reboot_tests              skip     skip     skip     same
pstore:pstore_tests                          fail     fail     fail     same
ptrace:peeksiginfo                           pass     pass     pass     same
ptrace:vmaccess                              fail     fail     fail     same
rseq:basic_percpu_ops_test                   pass     pass     pass     same
rseq:basic_test                              pass     pass     pass     same
rseq:param_test                              pass     pass     pass     same
rseq:param_test_benchmark                    pass     pass     pass     same
rseq:param_test_compare_twice                pass     pass     pass     same
rseq:run_param_test.sh                       fail     fail     fail     same
sgx:test_sgx                                 fail     fail     fail     same
sigaltstack:sas                              pass     pass     pass     same
size:get_size                                pass     pass     pass     same
splice:default_file_splice_read.sh           pass     pass     pass     same
static_keys:test_static_keys.sh              skip     skip     skip     same
tc-testing:tdc.sh                            pass     pass     pass     same
timens:clock_nanosleep                       pass     pass     pass     same
timens:exec                                  pass     pass     pass     same
timens:procfs                                pass     pass     pass     same
timens:timens                                pass     pass     pass     same
timens:timer                                 pass     pass     pass     same
timens:timerfd                               pass     pass     pass     same
timers:inconsistency-check                   fail     fail     fail     same
timers:mqueue-lat                            pass     pass     pass     same
timers:nanosleep                             pass     pass     pass     same
timers:nsleep-lat                            fail     fail     fail     same
timers:posix_timers                          pass     pass     pass     same
timers:rtcpie                                pass     pass     pass     same
timers:set-timer-lat                         fail     fail     fail     same
timers:threadtest                            pass     pass     pass     same
tpm2:test_smoke.sh                           fail     fail     fail     same
tpm2:test_space.sh                           fail     fail     fail     same
vm:run_vmtests                               fail     fail     fail     same
x86:amx_64                                   fail     fail     fail     same
x86:check_initial_reg_state_64               pass     pass     pass     same
x86:corrupt_xstate_header_64                 pass     pass     pass     same
x86:fsgsbase_64                              pass     pass     pass     same
x86:fsgsbase_restore_64                      pass     pass     pass     same
x86:ioperm_64                                pass     pass     pass     same
x86:iopl_64                                  pass     pass     pass     same
x86:mov_ss_trap_64                           pass     pass     pass     same
x86:mpx-mini-test_64                         fail     fail     fail     same
x86:protection_keys_64                       pass     pass     pass     same
x86:sigaltstack_64                           pass     pass     pass     same
x86:sigreturn_64                             pass     pass     pass     same
x86:single_step_syscall_64                   pass     pass     pass     same
x86:syscall_nt_64                            pass     pass     pass     same
x86:sysret_rip_64                            pass     pass     pass     same
x86:sysret_ss_attrs_64                       pass     pass     pass     same
x86:test_mremap_vdso_64                      pass     pass     pass     same
x86:test_vdso_64                             pass     pass     pass     same
x86:test_vsyscall_64                         pass     pass     pass     same
zram:zram.sh                                 pass     pass     pass     same

Specific sanity tests: passed

The patched kernel was compiled with these additional options:

CONFIG_CIFS_DEBUG2=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_RH_KABI_SIZE_ALIGN_CHECKS=n

The aim was to run at least some of the modified code paths and scan for the possible locking problems. Options CONFIG_DEBUG_SPINLOCK=y and CONFIG_DEBUG_MUTEXES=y enabled reporting common locking issues. Option CONFIG_CIFS_DEBUG2=y enabled extensive cifs logging, allowing for easier tracking of the walked code paths. Option CONFIG_RH_KABI_SIZE_ALIGN_CHECKS=n was necessary for the mutex debugging option.

Additional runtime setup of the LTS 8.6 machine:

echo 7 > /proc/fs/cifs/cifsFYI

See Documentation/filesystems/cifs/README:

cifsFYI functions as a bit mask. Setting it to 1 enables additional kernel
logging of various informational messages. 2 enables logging of non-zero
SMB return codes while 4 enables logging of requests that take longer
than one second to complete (except for byte range lock requests).

Also (see https://wiki.samba.org/index.php/LinuxCIFS_troubleshooting):

echo 'module cifs +p' > /sys/kernel/debug/dynamic_debug/control
echo 'file fs/cifs/* +p' > /sys/kernel/debug/dynamic_debug/control

The LTS 8.6 machine was set up as the cifs client, while the hosting Rocky 9 machine as cifs server.

Server configuration:

# mkdir -p /sambashare
# chmod 2770 /sambashare/
# semanage fcontext -at samba_share_t '/sambashare(/.*)?'
# restorecon -vvFR /sambashare
# dnf install samba samba-common -y
# systemctl restart smb
# systemctl restart nmb
# groupadd sales
# chgrp sales /sambashare/
# useradd -s /sbin/nologin -G sales calvin
# smbpasswd -a calvin   # redhat
# echo -e "[smbshare]
\tpath = /sambashare
\twrite list = @sales
" >> /etc/samba/smb.conf
# systemctl restart smb
# systemctl restart nmb
# systemctl stop firewalld

Client configuration:

# dnf install cifs-utils -y
# mkdir sales
# echo "username=calvin
password=redhat
" > smb-multiuser.txt
# mount -t cifs //192.168.122.1/smbshare sales -o credentials=smb-multiuser.txt,multiuser,sec=ntlmssp

A few moderately large files (~100 MB) were transferred from the server to the client and vice-versa without any issue. Next, a link failure was emulated by shutting down server's network interface to trigger reconnection attempt on the client's side. After a few minutes it was brought back. This resulted in the following logs on the LTS 8.6 machine:

[ 2134.097279] CIFS: fs/cifs/smb2pdu.c: In echo request
[ 2134.100057] CIFS: fs/cifs/transport.c: Sending smb: smb_len=72
[ 2134.103230] CIFS: fs/cifs/connect.c: \\192.168.122.1 send echo request: rc = 0
[ 2195.537730] CIFS: fs/cifs/smb2pdu.c: In echo request
[ 2195.540974] CIFS: fs/cifs/smb2pdu.c: Echo request failed: -11
[ 2195.544612] CIFS: fs/cifs/connect.c: \\192.168.122.1 send echo request: rc = -11
[ 2256.978215] CIFS: fs/cifs/smb2pdu.c: In echo request
[ 2256.981436] CIFS: fs/cifs/smb2pdu.c: Echo request failed: -11
[ 2256.985065] CIFS: fs/cifs/connect.c: \\192.168.122.1 send echo request: rc = -11
[ 2259.028296] CIFS: VFS: \\192.168.122.1 has not responded in 180 seconds. Reconnecting...
[ 2259.033612] CIFS: fs/cifs/connect.c: cifs_reconnect: will retry 1 target(s)
[ 2259.038125] CIFS: fs/cifs/connect.c: Mark tcp session as need reconnect
[ 2259.042281] CIFS: fs/cifs/connect.c: cifs_reconnect: marking sessions and tcons for reconnect
[ 2259.046039] CIFS: fs/cifs/connect.c: cifs_reconnect: tearing down socket
[ 2259.048943] CIFS: fs/cifs/connect.c: State: 0x3 Flags: 0x0
[ 2259.051361] CIFS: fs/cifs/connect.c: Post shutdown state: 0x3 Flags: 0x0
[ 2259.053690] CIFS: fs/cifs/connect.c: cifs_reconnect: moving mids to private list
[ 2259.055879] CIFS: fs/cifs/connect.c: cifs_reconnect: issuing mid callbacks
[ 2259.057912] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2259.060059] CIFS: fs/cifs/dns_resolve.c: dns_resolve_server_name_to_ip: probably server name is whole unc: \\192.168.122.1
[ 2259.062992] CIFS: address conversion returned 1 for 192.168.122.1
[ 2259.064279] CIFS: fs/cifs/dns_resolve.c: dns_resolve_server_name_to_ip: unc is IP, skipping dns upcall: 192.168.122.1
[ 2259.066486] CIFS: address conversion returned 1 for 192.168.122.1
[ 2259.067771] CIFS: fs/cifs/connect.c: generic_ip_connect: connecting to 192.168.122.1:445
[ 2259.069483] CIFS: fs/cifs/connect.c: Socket created
[ 2259.070517] CIFS: fs/cifs/connect.c: sndbuf 16384 rcvbuf 87380 rcvtimeo 0x1b58
[ 2260.050335] CIFS: fs/cifs/connect.c: Error -113 connecting to server
[ 2260.054542] CIFS: fs/cifs/connect.c: reconnect error -113
[ 2263.122273] CIFS: fs/cifs/dns_resolve.c: dns_resolve_server_name_to_ip: probably server name is whole unc: \\192.168.122.1
[ 2263.129476] CIFS: address conversion returned 1 for 192.168.122.1
[ 2263.133481] CIFS: fs/cifs/dns_resolve.c: dns_resolve_server_name_to_ip: unc is IP, skipping dns upcall: 192.168.122.1
[ 2263.139714] CIFS: address conversion returned 1 for 192.168.122.1
[...]
[ 2318.560957] CIFS: address conversion returned 1 for 192.168.122.1
[ 2318.563184] CIFS: fs/cifs/connect.c: generic_ip_connect: connecting to 192.168.122.1:445
[ 2318.566295] CIFS: fs/cifs/connect.c: Socket created
[ 2318.567947] CIFS: fs/cifs/connect.c: sndbuf 16384 rcvbuf 87380 rcvtimeo 0x1b58
[ 2318.570132] CIFS: fs/cifs/smb2ops.c: set credits to 1 due to smb2 reconnect
[ 2318.572192] CIFS: fs/cifs/smb2pdu.c: In echo request
[ 2318.573608] CIFS: fs/cifs/connect.c: \\192.168.122.1 send echo request: rc = 0
[ 2318.575717] CIFS: fs/cifs/smb2pdu.c: Need negotiate, reconnecting tcons
[ 2318.577626] CIFS: fs/cifs/smb2pdu.c: Negotiate protocol
[ 2318.578773] CIFS: fs/cifs/transport.c: Sending smb: smb_len=236
[ 2318.582771] CIFS: fs/cifs/connect.c: RFC1002 header 0x10c
[ 2318.583936] CIFS: fs/cifs/smb2misc.c: SMB2 data length 74 offset 128
[ 2318.585227] CIFS: fs/cifs/smb2misc.c: SMB2 len 202
[ 2318.586214] CIFS: fs/cifs/smb2misc.c: length of negcontexts 60 pad 6
[ 2318.587428] CIFS: fs/cifs/transport.c: cifs_sync_mid_result: cmd=0 mid=0 state=64
[ 2318.588694] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2318.590119] CIFS: fs/cifs/smb2pdu.c: mode 0x1
[ 2318.590973] CIFS: fs/cifs/smb2pdu.c: negotiated smb3.1.1 dialect
[ 2318.592086] CIFS: fs/cifs/asn1.c: OID len = 10 oid = 0x1 0x3 0x6 0x1
[ 2318.593219] CIFS: fs/cifs/smb2pdu.c: decoding 2 negotiate contexts
[ 2318.594314] CIFS: fs/cifs/smb2pdu.c: decode SMB3.11 encryption neg context of len 4
[ 2318.595617] CIFS: fs/cifs/smb2pdu.c: SMB311 cipher type:2
[ 2318.596541] CIFS: fs/cifs/connect.c: Free previous auth_key.response = 0000000042d1e2d6
[ 2318.597901] CIFS: fs/cifs/connect.c: Security Mode: 0x1 Capabilities: 0x300047 TimeAdjust: 0
[ 2318.599291] CIFS: fs/cifs/smb2pdu.c: Session Setup
[ 2318.600089] CIFS: fs/cifs/smb2pdu.c: sess setup type 4
[ 2318.600952] CIFS: fs/cifs/transport.c: Sending smb: smb_len=124
[ 2318.602307] CIFS: fs/cifs/connect.c: RFC1002 header 0xe8
[ 2318.603201] CIFS: fs/cifs/smb2misc.c: SMB2 data length 160 offset 72
[ 2318.604256] CIFS: fs/cifs/smb2misc.c: SMB2 len 232
[ 2318.605064] CIFS: fs/cifs/transport.c: cifs_sync_mid_result: cmd=1 mid=1 state=64
[ 2318.606304] CIFS: Status code returned 0xc0000016 STATUS_MORE_PROCESSING_REQUIRED
[ 2318.607562] CIFS: fs/cifs/smb2maperror.c: Mapping SMB2 status code 0xc0000016 to POSIX err -5
[ 2318.608918] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2318.610069] CIFS: fs/cifs/smb2pdu.c: rawntlmssp session setup challenge phase
[ 2318.611226] CIFS: fs/cifs/transport.c: Sending smb: smb_len=316
[ 2318.616604] CIFS: fs/cifs/connect.c: RFC1002 header 0x48
[ 2318.617669] CIFS: fs/cifs/smb2misc.c: SMB2 data length 0 offset 72
[ 2318.618750] CIFS: fs/cifs/smb2misc.c: SMB2 len 73
[ 2318.619571] CIFS: fs/cifs/smb2misc.c: Calculated size 73 length 72 mismatch mid 2
[ 2318.620880] CIFS: fs/cifs/transport.c: cifs_sync_mid_result: cmd=1 mid=2 state=64
[ 2318.622210] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2318.623522] CIFS: fs/cifs/smb2pdu.c: SMB2/3 session established successfully
[ 2318.624780] CIFS: fs/cifs/smb2pdu.c: TCON
[ 2318.625518] CIFS: fs/cifs/transport.c: Sending smb: smb_len=126
[ 2318.627342] CIFS: fs/cifs/connect.c: RFC1002 header 0x50
[ 2318.628349] CIFS: fs/cifs/smb2misc.c: SMB2 len 80
[ 2318.629228] CIFS: fs/cifs/smb2ops.c: add 64 credits total=195
[ 2318.630291] CIFS: fs/cifs/transport.c: cifs_sync_mid_result: cmd=3 mid=3 state=64
[ 2318.631683] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2318.632924] CIFS: fs/cifs/smb2pdu.c: connection to disk share
[ 2318.633987] CIFS: fs/cifs/smb2pdu.c: reconnect tcon rc = 0
[ 2318.634899] CIFS: fs/cifs/connect.c: cifs_put_tcon: tc_count=2
[ 2318.635862] CIFS: fs/cifs/smb2pdu.c: TCON
[ 2318.636560] CIFS: fs/cifs/transport.c: Sending smb: smb_len=118
[ 2318.638008] CIFS: fs/cifs/connect.c: RFC1002 header 0x50
[ 2318.638921] CIFS: fs/cifs/smb2misc.c: SMB2 len 80
[ 2318.639703] CIFS: fs/cifs/smb2ops.c: add 64 credits total=258
[ 2318.640716] CIFS: fs/cifs/transport.c: cifs_sync_mid_result: cmd=3 mid=4 state=64
[ 2318.641961] CIFS: fs/cifs/misc.c: Null buffer passed to cifs_small_buf_release
[ 2318.643222] CIFS: fs/cifs/smb2pdu.c: connection to pipe share
[ 2318.644232] CIFS: fs/cifs/smb2pdu.c: reconnect tcon rc = 0
[ 2318.645220] CIFS: fs/cifs/connect.c: cifs_put_smb_ses: ses_count=2
[ 2318.646322] CIFS: fs/cifs/connect.c: cifs_put_smb_ses: ses_count=2
[ 2318.647437] CIFS: fs/cifs/connect.c: cifs_put_smb_ses: ses ipc: \\192.168.122.1\IPC$
[ 2318.648788] CIFS: fs/cifs/smb2pdu.c: Reconnecting tcons finished

This message

CIFS: fs/cifs/connect.c: cifs_reconnect: will retry 1 target(s)

is printed by

cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
server->nr_targets);

in the cifs_reconnect() function addressed by the CVE-2023-53751 fix. Unfortunately it didn't trigger the reconn_set_next_dfs_target() call where server->hostname change occurs.

pvts-mat added 4 commits April 1, 2026 22:28
jira VULN-169227
cve-pre CVE-2023-53751
commit-author Vincent Whitchurch <vincent.whitchurch@axis.com>
commit cc391b6
upstream-diff |
  Multiple differences across many files due to large discrepancies
  between upstream and LTS 8.6, HOWEVER, the commit was obtained in the
  exact same way as on upstream, namely:
  1. locating all usages of `TCP_Server_Info::srv_mutex',
  2. changing all `mutex_lock(&<server_ptr>->srv_mutex)' to
     `cifs_server_lock(<server_ptr>)',
  3. changing all `mutex_unlock(&<server_ptr>->srv_mutex)' to
     `cifs_server_unlock(<server_ptr>)',
  4. renaming `srv_mutex~' to `_srv_mutex' to make sure nothing was
     missed.
  In this regard the commit doesn't differ from the upstream.

The srv_mutex is used during writeback so cifs should ensure that
allocations done when that mutex is held are done with GFP_NOFS, to
avoid having direct reclaim ending up waiting for the same mutex and
causing a deadlock.  This is detected by lockdep with the splat below:

 ======================================================
 WARNING: possible circular locking dependency detected
 5.18.0 ctrliq#70 Not tainted
 ------------------------------------------------------
 kswapd0/49 is trying to acquire lock:
 ffff8880195782e0 (&tcp_ses->srv_mutex){+.+.}-{3:3}, at: compound_send_recv

 but task is already holding lock:
 ffffffffa98e66c0 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat

 which lock already depends on the new lock.

 the existing dependency chain (in reverse order) is:

 -> ctrliq#1 (fs_reclaim){+.+.}-{0:0}:
        fs_reclaim_acquire
        kmem_cache_alloc_trace
        __request_module
        crypto_alg_mod_lookup
        crypto_alloc_tfm_node
        crypto_alloc_shash
        cifs_alloc_hash
        smb311_crypto_shash_allocate
        smb311_update_preauth_hash
        compound_send_recv
        cifs_send_recv
        SMB2_negotiate
        smb2_negotiate
        cifs_negotiate_protocol
        cifs_get_smb_ses
        cifs_mount
        cifs_smb3_do_mount
        smb3_get_tree
        vfs_get_tree
        path_mount
        __x64_sys_mount
        do_syscall_64
        entry_SYSCALL_64_after_hwframe

 -> #0 (&tcp_ses->srv_mutex){+.+.}-{3:3}:
        __lock_acquire
        lock_acquire
        __mutex_lock
        mutex_lock_nested
        compound_send_recv
        cifs_send_recv
        SMB2_write
        smb2_sync_write
        cifs_write
        cifs_writepage_locked
        cifs_writepage
        shrink_page_list
        shrink_lruvec
        shrink_node
        balance_pgdat
        kswapd
        kthread
        ret_from_fork

 other info that might help us debug this:

  Possible unsafe locking scenario:

        CPU0                    CPU1
        ----                    ----
   lock(fs_reclaim);
                                lock(&tcp_ses->srv_mutex);
                                lock(fs_reclaim);
   lock(&tcp_ses->srv_mutex);

  *** DEADLOCK ***

 1 lock held by kswapd0/49:
  #0: ffffffffa98e66c0 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat

 stack backtrace:
 CPU: 2 PID: 49 Comm: kswapd0 Not tainted 5.18.0 ctrliq#70
 Call Trace:
  <TASK>
  dump_stack_lvl
  dump_stack
  print_circular_bug.cold
  check_noncircular
  __lock_acquire
  lock_acquire
  __mutex_lock
  mutex_lock_nested
  compound_send_recv
  cifs_send_recv
  SMB2_write
  smb2_sync_write
  cifs_write
  cifs_writepage_locked
  cifs_writepage
  shrink_page_list
  shrink_lruvec
  shrink_node
  balance_pgdat
  kswapd
  kthread
  ret_from_fork
  </TASK>

Fix this by using the memalloc_nofs_save/restore APIs around the places
where the srv_mutex is held.  Do this in a wrapper function for the
lock/unlock of the srv_mutex, and rename the srv_mutex to avoid missing
call sites in the conversion.

Note that there is another lockdep warning involving internal crypto
locks, which was masked by this problem and is visible after this fix,
see the discussion in this thread:

 https://lore.kernel.org/all/20220523123755.GA13668@axis.com/

Link: https://lore.kernel.org/r/CANT5p=rqcYfYMVHirqvdnnca4Mo+JQSw5Qu12v=kPfpk5yhhmg@mail.gmail.com/
	Reported-by: Shyam Prasad N <nspmangalore@gmail.com>
	Suggested-by: Lars Persson <larper@axis.com>
	Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
	Reviewed-by: Enzo Matsumiya <ematsumiya@suse.de>
	Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
	Signed-off-by: Steve French <stfrench@microsoft.com>
(cherry picked from commit cc391b6)
	Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@cjr.nz>
commit 775e44d
upstream-diff Ignored the introduction of `pserver' variable, as well as
  the usage of `hostname' local, as they were only needed in the upstream
  because of the dual source of the server hostname, introduced in the
  non-backported commit 9de7499 ("smb3:
  use netname when available on secondary channels")

Serialise access of TCP_Server_Info::hostname in
assemble_neg_contexts() by holding the server's mutex otherwise it
might end up accessing an already-freed hostname pointer from
cifs_reconnect() or cifs_resolve_server().

	Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
	Reviewed-by: Enzo Matsumiya <ematsumiya@suse.de>
	Signed-off-by: Steve French <stfrench@microsoft.com>
(cherry picked from commit 775e44d)
	Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@cjr.nz>
commit 39a154f
upstream-diff Manually obtained commit. Upstream fixes appear in
  multiple places, but they all cover only the `cifs_tree_connect()'
  function (the CONFIG_CIFS_DFS_UPCALL=y variant) and its call tree. Same
  goes for the backported version, taking into account the lack of a major
  function rewrite in c88f7dc ("cifs:
  support nested dfs links over reconnect").
  Upstream call tree at the moment of the fix and the changes at each
  level:
    cifs_tree_connect()
          Put `cifs_server_{lock/unlock}(server)' around `scnprintf(...,
          server->hostname);' call.
    tree_connect_dfs_target()
          No changes.
    __tree_connect_dfs_target()
          Removed `extract_unc_hostname(server->hostname, ...)' call, used
          full `server->hostname' instead of its substring `tcp_host'.
    target_share_matches_server()
          Put `cifs_server_{lock/unlock}(server)' around
          `server->hostname' accesses in conditional expression and debug
          message composition before the `match_target_ip()' call.
    match_target_ip()
          Put `spin_{lock/unlock}(&server->srv_lock)' around
          `cifs_match_ipaddr(...server->dstaddr, ....)' call.
  Backported version call tree:
    cifs_tree_connect()
          - Put `cifs_server_{lock/unlock}(server)' around `scnprintf(...,
            server->hostname)' call - like in the upstream.
          - Put `cifs_server_{lock/unlock}(server)' around
            `server->hostname' (and its substring `tcp_host') access in
            conditional expression and debug message composition before
            the `match_target_ip()' call - like in the upstream. This
            required moving the `extract_unc_hostname()' call inside the
            loop. Unlike in the upstream it was not removed entirely,
            opting for functional equivalence between the patched and
            non-patched version instead of strictly preserving backported
            commit's changes; perhaps at the time of the upstream fix the
            equality `server->hostname' = `tcp_host' held true, allowing
            for the reduction of `extract_unc_hostname()' call, but that
            was not certain for the ciqlts8_6 version. If it wasn't true
            then the commit mixed a bugfix with a functional change which
            was simply filtered out here. Moving `extract_unc_hostname()'
            call inside the loop did not create any algorithmic
            inconsistencies which weren't already present (if any), merely
            preventing them from ending in bad memory access. In the
            upstream the value of `server->hostname' isn't saved to remain
            constant for all loop iterations in the
            `__tree_connect_dfs_target()' function either. A ngeligible
            performance penalty associated with the calculation redundancy
            was accepted given the simplicity of the fix it allowed.
    match_target_ip()
          Put `spin_{lock/unlock}(&cifs_tcp_ses_lock)' around the
          `cifs_match_ipaddr(...server->dstaddr, ...)' call - like in the
          upstream, except for the `cifs_tcp_ses_lock' instead of
          `server->srv_lock'. The latter was introduced in the
          non-backported commit d7d7a66
          ("cifs: avoid use of global locks for high contention
          data"). The same pattern of protecting `server->dstaddr' with
          `cifs_tcp_ses_lock' can be observed in ciqlts8_6 in the
          `reconn_set_ipaddr_from_hostname()' function.

Use the appropriate locks to protect access of hostname and dstaddr
fields in cifs_tree_connect() as they might get changed by other
tasks.

	Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
	Reviewed-by: Enzo Matsumiya <ematsumiya@suse.de>
	Signed-off-by: Steve French <stfrench@microsoft.com>
(cherry picked from commit 39a154f)
	Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
jira VULN-169227
cve CVE-2023-53751
commit-author Paulo Alcantara <pc@manguebit.com>
commit 90c49fc
upstream-diff |
  fs/cifs/cifsglob.h
        Added `srv_lock' to the `TCP_Server_Info' struct as it was done in
        the non-backported commit d7d7a66
        ("cifs: avoid use of global locks for high contention data").
  fs/cifs/cifs_debug.c
        Ignored changes in `cifs_debug_data_proc_show()' because the code
        using `hostname' was only added in the non-backported commit
        40f077a ("cifs: clarify hostname
        vs ip address in /proc/fs/cifs/DebugData").
  fs/cifs/connect.c
        - Changed the `reconn_set_next_dfs_target()' function instead of
          `__reconnect_target_unlocked()' which doesn't exist in LTS
          8.6. The `__reconnect_target_unlocked()' is where the `hostname'
          is "updated once or many times during reconnect" (see original
          commit message). The "reconnect" refers to the function
          `cifs_reconnect()', which calls `__reconnect_target_unlocked()'
          on a third level down the call tree.  Relative to LTS 8.6 the
          `cifs_reconnect()' underwent major rewrites in the commits
          bbcce36 ("cifs: split out dfs
          code from cifs_reconnect()") and
          c88f7dc ("cifs: support nested
          dfs links over reconnect"). In LTS 8.6 the updating of
          `hostname' can be tracked down to the
          `reconn_set_next_dfs_target()' function called by
          `cifs_reconnect()'.
        - Added initialization of `TCP_Server_Info::srv_lock' in
          `cifs_get_tcp_session()' as it's done in d7d7a66.
        - In `match_server()' changed the lockdep assertion from holding
          `server->srv_lock' to holding `cifs_tcp_ses_lock', because this
          is the actual invariant for the LTS 8.6 version.
        - In `match_server()' sandwiched the `strcasecmp(server->hostname,
          ...)' call in the lock/unlock pair of `server->srv_lock',
          because in LTS 8.6 the `match_server()' is not called with the
          `server->srv_lock' held and yet this is the lock chosen to
          protect the `server->hostname' field.
  fs/cifs/sess.c
        Ignored changes in `cifs_try_adding_channels()' because the code
        using `hostname' was only added in the non-backported commit
        9c2dc11 ("smb3: do not attempt
        multichannel to server which does not support it"). As a result no
        changes to the file have been made.

TCP_Server_Info::hostname may be updated once or many times during
reconnect, so protect its access outside reconnect path as well and
then prevent any potential use-after-free bugs.

	Cc: stable@vger.kernel.org
	Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
	Signed-off-by: Steve French <stfrench@microsoft.com>
(cherry picked from commit 90c49fc)
	Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
@pvts-mat pvts-mat force-pushed the ciqlts8_6-CVE-2023-53751 branch from 377e3a8 to ac27ef5 Compare April 1, 2026 20:28
@PlaidCat PlaidCat requested a review from a team April 1, 2026 20:39
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🤖 Validation Checks In Progress Workflow run: https://github.com/ctrliq/kernel-src-tree/actions/runs/23870172083

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🔍 Upstream Linux Kernel Commit Check

  • ❌ PR commit 601cd8f6b397 (cifs: fix race in assemble_neg_contexts()) references CVE-2023-53751 but
    upstream commit 775e44d6d86d has no CVE assigned

  • ❌ PR commit 9d7bba34b26e (cifs: protect access of TCP_Server_Info::{dstaddr,hostname}) references CVE-2023-53751 but
    upstream commit 39a154fc2d17 has no CVE assigned

This is an automated message from the kernel commit checker workflow.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

🔍 Interdiff Analysis

  • ⚠️ PR commit c746f8ae6270 (cifs: fix potential deadlock in direct reclaim) → upstream cc391b694ff0
    Differences found:
================================================================================
*    DELTA DIFFERENCES - code changes that differ between the patches          *
================================================================================

--- b/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -903,14 +903,14 @@
 {
 	struct cifs_ses *ses = sess_data->ses;
 
-	cifs_server_lock(ses->server);
+	mutex_lock(&ses->server->srv_mutex);
 	if (!ses->server->session_estab) {
 		if (ses->server->sign) {
 			ses->server->session_key.response =
 				kmemdup(ses->auth_key.response,
 				ses->auth_key.len, GFP_KERNEL);
 			if (!ses->server->session_key.response) {
-				cifs_server_unlock(ses->server);
+				mutex_unlock(&ses->server->srv_mutex);
 				return -ENOMEM;
 			}
 			ses->server->session_key.len =
@@ -919,7 +919,7 @@
 		ses->server->sequence_number = 0x2;
 		ses->server->session_estab = true;
 	}
-	cifs_server_unlock(ses->server);
+	mutex_unlock(&ses->server->srv_mutex);
 
 	cifs_dbg(FYI, "CIFS session established successfully\n");
 	spin_lock(&GlobalMid_Lock);

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################

--- b/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -465,7 +465,7 @@
 	int ret = 0;
 
 	/* Store the reconnect address */
-	mutex_lock(&tcon->ses->server->srv_mutex);
+	cifs_server_lock(tcon->ses->server);
 	if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr))
 		goto unlock;
 
@@ -501,7 +501,7 @@
 	cifs_signal_cifsd_for_reconnect(tcon->ses->server, false);
 
 unlock:
-	mutex_unlock(&tcon->ses->server->srv_mutex);
+	cifs_server_unlock(tcon->ses->server);
 
 	return ret;
 }
--- b/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -16,6 +16,7 @@
 #include <linux/mempool.h>
 #include <linux/workqueue.h>
 #include <linux/utsname.h>
+#include <linux/sched/mm.h>
 #include <linux/netfs.h>
 #include "cifs_fs_sb.h"
 #include "cifsacl.h"
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -148,7 +148,7 @@
 	struct TCP_Server_Info *server = container_of(work,
 					struct TCP_Server_Info, resolve.work);
 
-	mutex_lock(&server->srv_mutex);
+	cifs_server_lock(server);
 
 	/*
 	 * Resolve the hostname again to make sure that IP address is up-to-date.
@@ -159,7 +159,7 @@
 				__func__, rc);
 	}
 
-	mutex_unlock(&server->srv_mutex);
+	cifs_server_unlock(server);
 }
 
 /*
@@ -359,7 +359,7 @@
 
 	do {
 		try_to_freeze();
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 
 		if (!cifs_swn_set_server_dstaddr(server)) {
 			/* resolve the hostname again to make sure that IP address is up-to-date */
@@ -372,7 +372,7 @@
 		else
 			rc = generic_ip_connect(server);
 		if (rc) {
-			mutex_unlock(&server->srv_mutex);
+			cifs_server_unlock(server);
 			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
 			msleep(3000);
 		} else {
@@ -383,7 +383,7 @@
 				server->tcpStatus = CifsNeedNegotiate;
 			spin_unlock(&cifs_tcp_ses_lock);
 			cifs_swn_reset_server_dstaddr(server);
-			mutex_unlock(&server->srv_mutex);
+			cifs_server_unlock(server);
 			mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
 		}
 	} while (server->tcpStatus == CifsNeedReconnect);
@@ -488,7 +488,7 @@
 
 	do {
 		try_to_freeze();
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 
 		rc = reconnect_target_unlocked(server, &tl, &target_hint);
 		if (rc) {
@@ -492,7 +492,7 @@
 		rc = reconnect_target_unlocked(server, &tl, &target_hint);
 		if (rc) {
 			/* Failed to reconnect socket */
-			mutex_unlock(&server->srv_mutex);
+			cifs_server_unlock(server);
 			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
 			msleep(3000);
 			continue;
@@ -510,7 +510,7 @@
 			server->tcpStatus = CifsNeedNegotiate;
 		spin_unlock(&cifs_tcp_ses_lock);
 		cifs_swn_reset_server_dstaddr(server);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
 	} while (server->tcpStatus == CifsNeedReconnect);
 
--- b/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -1120,7 +1120,7 @@
 	struct cifs_ses *ses = sess_data->ses;
 	struct TCP_Server_Info *server = sess_data->server;
 
-	mutex_lock(&server->srv_mutex);
+	cifs_server_lock(server);
 	if (!server->session_estab) {
 		if (server->sign) {
 			server->session_key.response =
@@ -1124,7 +1124,7 @@
 				kmemdup(ses->auth_key.response,
 				ses->auth_key.len, GFP_KERNEL);
 			if (!server->session_key.response) {
-				mutex_unlock(&server->srv_mutex);
+				cifs_server_unlock(server);
 				return -ENOMEM;
 			}
 			server->session_key.len =
@@ -1136,7 +1136,7 @@
 		server->sequence_number = 0x2;
 		server->session_estab = true;
 	}
-	mutex_unlock(&server->srv_mutex);
+	cifs_server_unlock(server);
 
 	cifs_dbg(FYI, "CIFS session established successfully\n");
 	return 0;
--- b/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -1190,7 +1190,7 @@
 	if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
 		spin_unlock(&cifs_tcp_ses_lock);
 
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
 		mutex_unlock(&server->srv_mutex);
 
@@ -1192,7 +1192,7 @@
 
 		mutex_lock(&server->srv_mutex);
 		smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 
 		spin_lock(&cifs_tcp_ses_lock);
 	}
@@ -1266,7 +1266,7 @@
 			.iov_len = resp_iov[0].iov_len
 		};
 		spin_unlock(&cifs_tcp_ses_lock);
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		smb311_update_preauth_hash(ses, server, &iov, 1);
 		mutex_unlock(&server->srv_mutex);
 		spin_lock(&cifs_tcp_ses_lock);
@@ -1268,7 +1268,7 @@
 		spin_unlock(&cifs_tcp_ses_lock);
 		mutex_lock(&server->srv_mutex);
 		smb311_update_preauth_hash(ses, server, &iov, 1);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 		spin_lock(&cifs_tcp_ses_lock);
 	}
 	spin_unlock(&cifs_tcp_ses_lock);

================================================================================
*    CONTEXT DIFFERENCES - surrounding code differences between the patches    *
================================================================================

--- b/fs/cifs/cifs_swn.c
+++ b/fs/cifs/cifs_swn.c
@@ -462,7 +462,7 @@
-static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *addr)
-{
+	int ret = 0;
+
 	/* Store the reconnect address */
 	mutex_lock(&tcon->ses->server->srv_mutex);
-	if (!cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr)) {
-		int ret;
+	if (cifs_sockaddr_equal(&tcon->ses->server->dstaddr, addr))
+		goto unlock;
 
@@ -515,7 +498,7 @@
-			tcon->ses->server->tcpStatus = CifsNeedReconnect;
-		spin_unlock(&GlobalMid_Lock);
-	}
+	cifs_signal_cifsd_for_reconnect(tcon->ses->server, false);
+
+unlock:
 	mutex_unlock(&tcon->ses->server->srv_mutex);
 
-	return 0;
+	return ret;
 }
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -288,6 +293,6 @@
-		mid_entry->mid_flags |= MID_DELETED;
+		mid->mid_flags |= MID_DELETED;
 	}
 	spin_unlock(&GlobalMid_Lock);
 	mutex_unlock(&server->srv_mutex);
 
 	cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
@@ -309,8 +315,7 @@
 
 	do {
 		try_to_freeze();
-
 		mutex_lock(&server->srv_mutex);
 
-#ifdef CONFIG_CIFS_SWN_UPCALL
-		if (server->use_swn_dstaddr) {
+		if (!cifs_swn_set_server_dstaddr(server)) {
+			/* resolve the hostname again to make sure that IP address is up-to-date */
@@ -349,6 +424,6 @@
 		if (rc) {
-			cifs_dbg(FYI, "reconnect error %d\n", rc);
+			/* Failed to reconnect socket */
 			mutex_unlock(&server->srv_mutex);
+			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
 			msleep(3000);
-		} else {
-			atomic_inc(&tcpSesReconnectCount);
+			continue;
@@ -355,7 +430,7 @@
-#ifdef CONFIG_CIFS_SWN_UPCALL
-			server->use_swn_dstaddr = false;
-#endif
-			mutex_unlock(&server->srv_mutex);
-		}
+			server->tcpStatus = CifsNeedNegotiate;
+		spin_unlock(&cifs_tcp_ses_lock);
+		cifs_swn_reset_server_dstaddr(server);
+		mutex_unlock(&server->srv_mutex);
+		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
 	} while (server->tcpStatus == CifsNeedReconnect);
 
--- b/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -900,12 +900,13 @@
 	struct cifs_ses *ses = sess_data->ses;
+	struct TCP_Server_Info *server = sess_data->server;
 
-	mutex_lock(&ses->server->srv_mutex);
-	if (!ses->server->session_estab) {
-		if (ses->server->sign) {
-			ses->server->session_key.response =
+	mutex_lock(&server->srv_mutex);
+	if (!server->session_estab) {
+		if (server->sign) {
+			server->session_key.response =
 				kmemdup(ses->auth_key.response,
 				ses->auth_key.len, GFP_KERNEL);
-			if (!ses->server->session_key.response) {
-				mutex_unlock(&ses->server->srv_mutex);
+			if (!server->session_key.response) {
+				mutex_unlock(&server->srv_mutex);
 				return -ENOMEM;
 			}
@@ -913,7 +913,8 @@
-		ses->server->sequence_number = 0x2;
-		ses->server->session_estab = true;
+			server->session_key.len =
+		server->sequence_number = 0x2;
+		server->session_estab = true;
 	}
-	mutex_unlock(&ses->server->srv_mutex);
+	mutex_unlock(&server->srv_mutex);
 
 	cifs_dbg(FYI, "CIFS session established successfully\n");
-	spin_lock(&GlobalMid_Lock);
+	return 0;
--- b/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1261,9 +1261,9 @@
 	struct cifs_ses *ses = sess_data->ses;
-	struct TCP_Server_Info *server = cifs_ses_server(ses);
+	struct TCP_Server_Info *server = sess_data->server;
 
 	mutex_lock(&server->srv_mutex);
 	if (server->ops->generate_signingkey) {
-		rc = server->ops->generate_signingkey(ses);
+		rc = server->ops->generate_signingkey(ses, server);
 		if (rc) {
 			cifs_dbg(FYI,
 				"SMB3 session key generation failed\n");
@@ -1281,4 +1386,4 @@
 	mutex_unlock(&server->srv_mutex);
 
 	cifs_dbg(FYI, "SMB2/3 session established successfully\n");
-	/* keep existing ses state if binding */
+	return rc;

================================================================================
*    ONLY IN PATCH2 - files not modified by patch1                             *
================================================================================

--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1327,9 +1327,9 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
 		cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n",
 			 __func__, ip);
 	} else {
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 	}
 
 	kfree(ip);
  • ⚠️ PR commit 601cd8f6b397 (cifs: fix race in assemble_neg_contexts()) → upstream 775e44d6d86d
    Differences found:
================================================================================
*    DELTA DIFFERENCES - code changes that differ between the patches          *
================================================================================

--- b/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -549,10 +549,8 @@
 	} else
 		req->NegotiateContextCount = cpu_to_le16(4);
 
-	cifs_server_lock(server);
 	ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
 					server->hostname);
-	cifs_server_unlock(server);
 	*total_len += ctxt_len;
 	pneg_ctxt += ctxt_len;
 

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################

--- b/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -541,8 +541,6 @@
 assemble_neg_contexts(struct smb2_negotiate_req *req,
 		      struct TCP_Server_Info *server, unsigned int *total_len)
 {
-	char *pneg_ctxt;
-	char *hostname = NULL;
 	unsigned int ctxt_len, neg_context_count;
 
 	if (*total_len > 200) {
@@ -544,6 +542,9 @@
 	char *pneg_ctxt;
 	char *hostname = NULL;
 	unsigned int ctxt_len, neg_context_count;
+	struct TCP_Server_Info *pserver;
+	char *pneg_ctxt;
+	char *hostname;
 
 	if (*total_len > 200) {
 		/* In case length corrupted don't want to overrun smb buffer */
@@ -574,8 +575,9 @@
 	 * secondary channels don't have the hostname field populated
 	 * use the hostname field in the primary channel instead
 	 */
-	hostname = CIFS_SERVER_IS_CHAN(server) ?
-		server->primary_server->hostname : server->hostname;
+	pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
+	cifs_server_lock(pserver);
+	hostname = pserver->hostname;
 	if (hostname && (hostname[0] != 0)) {
 		ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
 					      hostname);
@@ -584,6 +586,7 @@
 		neg_context_count = 3;
 	} else
 		neg_context_count = 2;
+	cifs_server_unlock(pserver);
 
 	build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
 	*total_len += sizeof(struct smb2_posix_neg_context);

================================================================================
*    CONTEXT DIFFERENCES - surrounding code differences between the patches    *
================================================================================

--- b/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -546,8 +572,3 @@
 	} else
-		req->NegotiateContextCount = cpu_to_le16(4);
-
-	ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
-					server->hostname);
-	*total_len += ctxt_len;
-	pneg_ctxt += ctxt_len;
+		neg_context_count = 2;
  • ⚠️ PR commit 9d7bba34b26e (cifs: protect access of TCP_Server_Info::{dstaddr,hostname}) → upstream 39a154fc2d17
    Differences found:
================================================================================
*    DELTA DIFFERENCES - code changes that differ between the patches          *
================================================================================

--- b/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1123,10 +1123,8 @@
 		goto out;
 	}
 
-	spin_lock(&cifs_tcp_ses_lock);
 	*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr,
 				    &tipaddr);
-	spin_unlock(&cifs_tcp_ses_lock);
 	cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
 	rc = 0;
 

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################

--- b/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1277,6 +1277,7 @@
 	if (rc < 0)
 		return rc;
 
+	spin_lock(&server->srv_lock);
 	*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
 	cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
 	return 0;
@@ -1278,6 +1279,7 @@
 		return rc;
 
 	*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
+	spin_unlock(&server->srv_lock);
 	cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
 	return 0;
 }

================================================================================
*    CONTEXT DIFFERENCES - surrounding code differences between the patches    *
================================================================================

--- b/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1120,6 +1122,5 @@
 
-	*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr,
-				    &tipaddr);
+	*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
 	cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
-	rc = 0;
-
+	return 0;
+}

================================================================================
*    ONLY IN PATCH1 - files not modified by patch2                             *
================================================================================

--- b/fs/cifs/connect.c
+++ a/fs/cifs/connect.c
@@ -4081,9 +4081,7 @@
 
 	if (!tcon->dfs_path) {
 		if (tcon->ipc) {
-			cifs_server_lock(server);
 			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
-			cifs_server_unlock(server);
 			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
 		} else {
 			rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
@@ -4097,6 +4095,8 @@
 	isroot = ref.server_type == DFS_TYPE_ROOT;
 	free_dfs_info_param(&ref);
 
+	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
+
 	for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
 		bool target_match;
 
@@ -4114,13 +4114,10 @@
 
 		extract_unc_hostname(share, &dfs_host, &dfs_host_len);
 
-		cifs_server_lock(server);
-		extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
 		if (dfs_host_len != tcp_host_len
 		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
 			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
 				 dfs_host, (int)tcp_host_len, tcp_host);
-			cifs_server_unlock(server);
 
 			rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
 			if (rc) {
@@ -4132,8 +4129,7 @@
 				cifs_dbg(FYI, "%s: skipping target\n", __func__);
 				continue;
 			}
+		}
-		} else
-			cifs_server_unlock(server);
 
 		if (tcon->ipc) {
 			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);

================================================================================
*    ONLY IN PATCH2 - files not modified by patch1                             *
================================================================================

--- a/fs/cifs/dfs.c
+++ b/fs/cifs/dfs.c
@@ -327,8 +327,8 @@ static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb
 	return rc;
 }
 
-static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host,
-				       size_t tcp_host_len, char *share, bool *target_match)
+static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
+				       bool *target_match)
 {
 	int rc = 0;
 	const char *dfs_host;
@@ -338,13 +338,16 @@ static int target_share_matches_server(struct TCP_Server_Info *server, const cha
 	extract_unc_hostname(share, &dfs_host, &dfs_host_len);
 
 	/* Check if hostnames or addresses match */
-	if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
-		cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
-			 dfs_host, (int)tcp_host_len, tcp_host);
+	cifs_server_lock(server);
+	if (dfs_host_len != strlen(server->hostname) ||
+	    strncasecmp(dfs_host, server->hostname, dfs_host_len)) {
+		cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__,
+			 (int)dfs_host_len, dfs_host, server->hostname);
 		rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
 		if (rc)
 			cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
 	}
+	cifs_server_unlock(server);
 	return rc;
 }
 
@@ -358,13 +361,9 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
 	struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
 	struct cifs_tcon *ipc = root_ses->tcon_ipc;
 	char *share = NULL, *prefix = NULL;
-	const char *tcp_host;
-	size_t tcp_host_len;
 	struct dfs_cache_tgt_iterator *tit;
 	bool target_match;
 
-	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
-
 	tit = dfs_cache_get_tgt_iterator(tl);
 	if (!tit) {
 		rc = -ENOENT;
@@ -387,8 +386,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
 			break;
 		}
 
-		rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
-						 &target_match);
+		rc = target_share_matches_server(server, share, &target_match);
 		if (rc)
 			break;
 		if (!target_match) {
@@ -497,7 +495,9 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
 	}
 
 	if (tcon->ipc) {
+		cifs_server_lock(server);
 		scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
+		cifs_server_unlock(server);
 		rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
 		goto out;
 	}
  • ⚠️ PR commit ac27ef517858 (cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname) → upstream 90c49fce1c43
    Differences found:
================================================================================
*    DELTA DIFFERENCES - code changes that differ between the patches          *
================================================================================

--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -147,11 +147,9 @@
 
 	name = dfs_cache_get_tgt_name(*tgt_it);
 
-	spin_lock(&server->srv_lock);
 	kfree(server->hostname);
 
 	server->hostname = extract_hostname(name);
-	spin_unlock(&server->srv_lock);
 	if (IS_ERR(server->hostname)) {
 		cifs_dbg(FYI,
 			 "%s: failed to extract hostname from target: %ld\n",
@@ -1177,7 +1175,7 @@
 {
 	struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
 
-	lockdep_assert_held(&cifs_tcp_ses_lock);
+	lockdep_assert_held(&server->srv_lock);
 
 	if (ctx->nosharesock)
 		return 0;
@@ -1196,12 +1194,8 @@
 	if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
 		return 0;
 
-	spin_lock(&server->srv_lock);
-	if (strcasecmp(server->hostname, ctx->server_hostname)) {
-		spin_unlock(&server->srv_lock);
+	if (strcasecmp(server->hostname, ctx->server_hostname))
 		return 0;
-	}
-	spin_unlock(&server->srv_lock);
 
 	if (!match_address(server, addr,
 			   (struct sockaddr *)&ctx->srcaddr))
@@ -1349,7 +1343,6 @@
 	tcp_ses->lstrp = jiffies;
 	tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
 	spin_lock_init(&tcp_ses->req_lock);
-	spin_lock_init(&tcp_ses->srv_lock);
 	INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
 	INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
 	INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################

--- b/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -280,6 +280,7 @@
 		seq_printf(m, "\n%d) ConnectionId: 0x%llx ",
 			c, server->conn_id);
 
+		spin_lock(&server->srv_lock);
 		if (server->hostname)
 			seq_printf(m, "Hostname: %s ", server->hostname);
 #ifdef CONFIG_CIFS_SMB_DIRECT
@@ -282,6 +283,7 @@
 
 		if (server->hostname)
 			seq_printf(m, "Hostname: %s ", server->hostname);
+		spin_unlock(&server->srv_lock);
 #ifdef CONFIG_CIFS_SMB_DIRECT
 		if (!server->rdma)
 			goto skip_rdma;
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -403,6 +403,7 @@
 		if (server->hostname != target) {
 			hostname = extract_hostname(target);
 			if (!IS_ERR(hostname)) {
+				spin_lock(&server->srv_lock);
 				kfree(server->hostname);
 				server->hostname = hostname;
 			} else {
@@ -405,6 +406,7 @@
 			if (!IS_ERR(hostname)) {
 				kfree(server->hostname);
 				server->hostname = hostname;
+				spin_unlock(&server->srv_lock);
 			} else {
 				cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
 					 __func__, PTR_ERR(hostname));

================================================================================
*    CONTEXT DIFFERENCES - surrounding code differences between the patches    *
================================================================================

--- b/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -614,5 +628,5 @@
 					atomic_read(&server->smb2slowcmd[j]),
 					server->hostname, j);
 #endif /* STATS2 */
-		list_for_each(tmp2, &server->smb_ses_list) {
-			ses = list_entry(tmp2, struct cifs_ses,
+		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+			list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -424,3 +567,2 @@
 
-#ifdef CONFIG_CIFS_SWN_UPCALL
 	/* Check witness registrations */
@@ -1181,2 +1408,4 @@
 		return 0;
+	}
+	spin_unlock(&server->srv_lock);
 

================================================================================
*    ONLY IN PATCH1 - files not modified by patch2                             *
================================================================================

--- b/fs/cifs/cifsglob.h
+++ a/fs/cifs/cifsglob.h
@@ -580,7 +580,6 @@
 struct TCP_Server_Info {
 	struct list_head tcp_ses_list;
 	struct list_head smb_ses_list;
-	spinlock_t srv_lock;  /* protect anything here that is not protected */
 	int srv_count; /* reference counter */
 	/* 15 character server name + 0x20 16th byte indicating type = srv */
 	char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];

================================================================================
*    ONLY IN PATCH2 - files not modified by patch1                             *
================================================================================

--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -159,6 +159,7 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
 /* returns number of channels added */
 int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
 {
+	struct TCP_Server_Info *server = ses->server;
 	int old_chan_count, new_chan_count;
 	int left;
 	int rc = 0;
@@ -178,16 +179,16 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
 		return 0;
 	}
 
-	if (ses->server->dialect < SMB30_PROT_ID) {
+	if (server->dialect < SMB30_PROT_ID) {
 		spin_unlock(&ses->chan_lock);
 		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
 		return 0;
 	}
 
-	if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
+	if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
 		ses->chan_max = 1;
 		spin_unlock(&ses->chan_lock);
-		cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
+		cifs_server_dbg(VFS, "no multichannel support\n");
 		return 0;
 	}
 	spin_unlock(&ses->chan_lock);

This is an automated interdiff check for backported commits.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

JIRA PR Check Results

4 commit(s) with issues found:

Commit ac27ef517858

Summary: cifs: fix potential use-after-free bugs in TCP_Server_Info::hostname

❌ Errors:

  • VULN-169227: Status is 'To Do', expected 'In Progress'

⚠️ Warnings:

  • VULN-169227: No time logged - please log time manually

Commit 9d7bba34b26e

Summary: cifs: protect access of TCP_Server_Info::{dstaddr,hostname}

❌ Errors:

  • VULN-169227: Status is 'To Do', expected 'In Progress'

⚠️ Warnings:

  • VULN-169227: No time logged - please log time manually

Commit 601cd8f6b397

Summary: cifs: fix race in assemble_neg_contexts()

❌ Errors:

  • VULN-169227: Status is 'To Do', expected 'In Progress'

⚠️ Warnings:

  • VULN-169227: No time logged - please log time manually

Commit c746f8ae6270

Summary: cifs: fix potential deadlock in direct reclaim

❌ Errors:

  • VULN-169227: Status is 'To Do', expected 'In Progress'

⚠️ Warnings:

  • VULN-169227: No time logged - please log time manually

Summary: Checked 4 commit(s) total.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

Validation checks completed with issues View full results: https://github.com/ctrliq/kernel-src-tree/actions/runs/23870172083

@pvts-mat
Copy link
Copy Markdown
Contributor Author

pvts-mat commented Apr 1, 2026

🔍 Upstream Linux Kernel Commit Check

* ❌ PR commit `601cd8f6b397 (cifs: fix race in assemble_neg_contexts())` references `CVE-2023-53751` but
  upstream commit `775e44d6d86d` has no CVE assigned

* ❌ PR commit `9d7bba34b26e (cifs: protect access of TCP_Server_Info::{dstaddr,hostname})` references `CVE-2023-53751` but
  upstream commit `39a154fc2d17` has no CVE assigned

This is an automated message from the kernel commit checker workflow.

See "Overview"

@PlaidCat
Copy link
Copy Markdown
Collaborator

PlaidCat commented Apr 2, 2026

[WIP] Reviewer Notes:
I will continue to update this and delete this line and [WIP] when done.

⚠️ PR commit c746f8a (cifs: fix potential deadlock in direct reclaim) → upstream cc391b6
Differences found:

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -359,7 +359,7 @@
 
 	do {
 		try_to_freeze();
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 
 		if (!cifs_swn_set_server_dstaddr(server)) {
 			/* resolve the hostname again to make sure that IP address is up-to-date */

This delta :
c746f8a#diff-9da6c9cec14d939ab18e7037291932a32d5b394f6b74e1eb18b9c77909701bb6L310-L314
Is because of the this missing re-write cited else where as well: this is correct but the offsets make it weird + the blank ling after try_to_freeze()

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -372,7 +372,7 @@
 		else
 			rc = generic_ip_connect(server);
 		if (rc) {
-			mutex_unlock(&server->srv_mutex);
+			cifs_server_unlock(server);
 			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
 			msleep(3000);
 		} else {

This got reordered in cifs: fix potential deadlock in direct reclaim for unmentioned reasons this is fine

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################
--- b/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -510,7 +510,7 @@
 			server->tcpStatus = CifsNeedNegotiate;
 		spin_unlock(&cifs_tcp_ses_lock);
 		cifs_swn_reset_server_dstaddr(server);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
 	} while (server->tcpStatus == CifsNeedReconnect);

This one is due to yet again cifs: fix potential deadlock in direct reclaim and the delta is due to some just absolute weirdness, the is the correct adaptation.
c746f8a#diff-9da6c9cec14d939ab18e7037291932a32d5b394f6b74e1eb18b9c77909701bb6L364-L370

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################
--- b/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -1120,7 +1120,7 @@
 	struct cifs_ses *ses = sess_data->ses;
 	struct TCP_Server_Info *server = sess_data->server;
 
-	mutex_lock(&server->srv_mutex);
+	cifs_server_lock(server);
 	if (!server->session_estab) {
 		if (server->sign) {
 			server->session_key.response =
@@ -1124,7 +1124,7 @@
 				kmemdup(ses->auth_key.response,
 				ses->auth_key.len, GFP_KERNEL);
 			if (!server->session_key.response) {
-				mutex_unlock(&server->srv_mutex);
+				cifs_server_unlock(server);
 				return -ENOMEM;
 			}
 			server->session_key.len =
@@ -1136,7 +1136,7 @@
 		server->sequence_number = 0x2;
 		server->session_estab = true;
 	}
-	mutex_unlock(&server->srv_mutex);
+	cifs_server_unlock(server);
 
 	cifs_dbg(FYI, "CIFS session established successfully\n");
 	return 0;

This correct due to missing cifs: use the chans_need_reconnect bitmap for reconnect status f486ef8
and the itroduction of the server variable.

================================================================================
*    ONLY IN PATCH2 - files not modified by patch1                             *
================================================================================

--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -1327,9 +1327,9 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
 		cifs_dbg(VFS, "%s: failed to convert address \'%s\'. skip address matching.\n",
 			 __func__, ip);
 	} else {
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, &sa);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 	}
 
 	kfree(ip);

This is expected due to missing cifs: support share failover when remounting b623661

@PlaidCat
Copy link
Copy Markdown
Collaborator

PlaidCat commented Apr 2, 2026

I'm making this one independent as there is a discussion to be had here:

################################################################################
!    REJECTED PATCH2 HUNKS - could not be compared; manual review needed       !
################################################################################
--- b/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -1190,7 +1190,7 @@
 	if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
 		spin_unlock(&cifs_tcp_ses_lock);
 
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
 		mutex_unlock(&server->srv_mutex);
 
@@ -1192,7 +1192,7 @@
 
 		mutex_lock(&server->srv_mutex);
 		smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 
 		spin_lock(&cifs_tcp_ses_lock);
 	}
@@ -1266,7 +1266,7 @@
 			.iov_len = resp_iov[0].iov_len
 		};
 		spin_unlock(&cifs_tcp_ses_lock);
-		mutex_lock(&server->srv_mutex);
+		cifs_server_lock(server);
 		smb311_update_preauth_hash(ses, server, &iov, 1);
 		mutex_unlock(&server->srv_mutex);
 		spin_lock(&cifs_tcp_ses_lock);
@@ -1268,7 +1268,7 @@
 		spin_unlock(&cifs_tcp_ses_lock);
 		mutex_lock(&server->srv_mutex);
 		smb311_update_preauth_hash(ses, server, &iov, 1);
-		mutex_unlock(&server->srv_mutex);
+		cifs_server_unlock(server);
 		spin_lock(&cifs_tcp_ses_lock);
 	}
 	spin_unlock(&cifs_tcp_ses_lock);

This is because we're missing two commits specifically the mutex_unlock() -> cifs_server_unlock() due to this missing commit: cifs: Fix preauth hash corruption 05946d4
Fixing smb311 hash corruption seems pretty important:
@kerneltoast @jallisonciq I would love your thoughts on this to me this seems like its low impact with just better safety.

But a follow on to that is additional locking fixups cifs: take cifs_tcp_ses_lock for status checks github.com/ctrliq/kernel-src-tree/commit/080dc5e5656c1cc1cdefb501b9b645a07519f763
this i'm less enthused about attempting to take but the "While I was there" BTW at the end of the comment makes me think we should think about it at least.

Copy link
Copy Markdown

@jallisonciq jallisonciq left a comment

Choose a reason for hiding this comment

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

I think these patches make the code better, and address the specific CVE called out. I don't think they fix everything, but they don't leave the code in a worse place. So on balance, +1 from me.

@PlaidCat
Copy link
Copy Markdown
Collaborator

PlaidCat commented Apr 3, 2026

I don't think they fix everything, but they don't leave the code in a worse place.

It appears to me that its turtles all the way down as some things are only addressed as WIWT fixes. Probably works fine for mainline but its hard to handle with backporting to old versions of the kernel.

@jallisonciq
Copy link
Copy Markdown

OK, I just wanted to let you know I've rebuilt this kernel and done a basic SMB3 mount with it, and that cifsfs filesystem still works.

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