18 ksmbd server fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmnqdE8ACgkQiiy9cAdy
 T1E4uwwAtRDds+fqFZHgqEW/0Vd8O1RJCvUGomoampb4z9rzHOMrekofAjRT6OJs
 M6jVx5L/22TT9Vf+Ya+WMrOkxQbjyFy8j6IjdefJi2SxX5Z9QM7ZvEWhQlDhdVUV
 Hfb7Zd3jsdDk6GvvIfVzlEXMLbWtkD5zhGYCVOfNuh/RlyGy+orkjbUfbGEI56c4
 WPXkVUvGqHnniU/AB4/9pDFFMOwy4IAY9Bs8u2b65FWoxDsPFbz8ntJ1+Ehcy+Er
 Try0JqSQT7uJNHN7O334NeylbsxLyszkqDyYUv3A8un7Txzi4OIZFNJuHE4Av95S
 XVbmrkCgZ7Bm4wvPBxc35usZk+7WFdIgLM5vA37pG93zQ/n/zdjTdOiGQ6+8qw/L
 rHgG6A1ti6/f48Lk5vj01fOcCoNIEBcwVYtajSGU45f44cGjyqgnDe0Id8OZe1yY
 DMhsthL45kK1mmYPaq9h85mdxCeec3aKTrMd79dgwiyVMIFEeVJwd6Yvs8SH6qFb
 +OSVq87r
 =WKB0
 -----END PGP SIGNATURE-----

Merge tag 'v7.1-rc-part2-ksmbd-fixes' of git://git.samba.org/ksmbd

Pull more smb server updates from Steve French:

 - move fs/smb/common/smbdirect to fs/smb/smbdirect

 - change signature calc to use AES-CMAC library, simpler and faster

 - invalid signature fix

 - multichannel fix

 - open create options fix

 - fix durable handle leak

 - cap maximum lock count to avoid potential denial of service

 - four connection fixes: connection free and session destroy IDA fixes,
   refcount fix, connection leak fix, max_connections off by one fix

 - IPC validation fix

 - fix out of bounds write in getting xattrs

 - fix use after free in durable handle reconnect

 - three ACL fixes: fix potential ACL overflow, harden num_aces check,
   and fix minimum ACE size check

* tag 'v7.1-rc-part2-ksmbd-fixes' of git://git.samba.org/ksmbd:
  smb: smbdirect: move fs/smb/common/smbdirect/ to fs/smb/smbdirect/
  smb: server: stop sending fake security descriptors
  ksmbd: scope conn->binding slowpath to bound sessions only
  ksmbd: fix CreateOptions sanitization clobbering the whole field
  ksmbd: fix durable fd leak on ClientGUID mismatch in durable v2 open
  ksmbd: fix O(N^2) DoS in smb2_lock via unbounded LockCount
  ksmbd: destroy async_ida in ksmbd_conn_free()
  ksmbd: destroy tree_conn_ida in ksmbd_session_destroy()
  ksmbd: Use AES-CMAC library for SMB3 signature calculation
  ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()
  ksmbd: fix out-of-bounds write in smb2_get_ea() EA alignment
  ksmbd: use check_add_overflow() to prevent u16 DACL size overflow
  ksmbd: fix use-after-free in smb2_open during durable reconnect
  ksmbd: validate num_aces and harden ACE walk in smb_inherit_dacl()
  smb: server: fix max_connections off-by-one in tcp accept path
  ksmbd: require minimum ACE size in smb_check_perm_dacl()
  ksmbd: validate response sizes in ipc_validate_msg()
  smb: server: fix active_num_conn leak on transport allocation failure
This commit is contained in:
Linus Torvalds 2026-04-23 17:04:18 -07:00
commit bd1886d6e4
40 changed files with 165 additions and 215 deletions

View File

@ -24662,7 +24662,7 @@ L: linux-cifs@vger.kernel.org
L: samba-technical@lists.samba.org (moderated for non-subscribers)
S: Maintained
F: fs/smb/client/smbdirect.*
F: fs/smb/common/smbdirect/
F: fs/smb/smbdirect/
F: fs/smb/server/transport_rdma.*
SMC91x ETHERNET DRIVER

View File

@ -4,7 +4,7 @@
source "fs/smb/client/Kconfig"
source "fs/smb/server/Kconfig"
source "fs/smb/common/smbdirect/Kconfig"
source "fs/smb/smbdirect/Kconfig"
config SMBFS
tristate

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SMBFS) += common/
obj-$(CONFIG_SMBDIRECT) += smbdirect/
obj-$(CONFIG_CIFS) += client/
obj-$(CONFIG_SMB_SERVER) += server/

View File

@ -182,7 +182,7 @@ config CIFS_SMB_DIRECT
bool "SMB Direct support"
depends on CIFS && INFINIBAND && INFINIBAND_ADDR_TRANS
depends on CIFS=m || INFINIBAND=y
select SMB_COMMON_SMBDIRECT
select SMBDIRECT
help
Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1.
SMB Direct allows transferring SMB packets over RDMA. If unsure,

View File

@ -9,7 +9,7 @@
#include "cifs_debug.h"
#include "cifsproto.h"
#include "smb2proto.h"
#include "../common/smbdirect/smbdirect_public.h"
#include "../smbdirect/public.h"
/* Port numbers for SMBD transport */
#define SMB_PORT 445

View File

@ -12,7 +12,7 @@
#include "cifsglob.h"
#include "../common/smbdirect/smbdirect.h"
#include "../smbdirect/smbdirect.h"
extern int rdma_readwrite_threshold;
extern int smbd_max_frmr_depth;

View File

@ -4,4 +4,3 @@
#
obj-$(CONFIG_SMBFS) += cifs_md4.o
obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect/

View File

@ -1,18 +0,0 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for smbdirect support
#
obj-$(CONFIG_SMB_COMMON_SMBDIRECT) += smbdirect.o
smbdirect-y := \
smbdirect_socket.o \
smbdirect_connection.o \
smbdirect_mr.o \
smbdirect_rw.o \
smbdirect_debug.o \
smbdirect_connect.o \
smbdirect_listen.o \
smbdirect_accept.o \
smbdirect_devices.o \
smbdirect_main.o

View File

@ -7,13 +7,13 @@ config SMB_SERVER
select NLS_UTF8
select NLS_UCS2_UTILS
select CRYPTO
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_DES
select CRYPTO_LIB_MD5
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_SHA512
select CRYPTO_LIB_UTILS
select CRYPTO_CMAC
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
@ -49,7 +49,7 @@ config SMB_SERVER_SMBDIRECT
bool "Support for SMB Direct protocol"
depends on SMB_SERVER && INFINIBAND && INFINIBAND_ADDR_TRANS
depends on SMB_SERVER=m || INFINIBAND=y
select SMB_COMMON_SMBDIRECT
select SMBDIRECT
default n
help

View File

@ -11,8 +11,8 @@
#include <linux/writeback.h>
#include <linux/uio.h>
#include <linux/xattr.h>
#include <crypto/hash.h>
#include <crypto/aead.h>
#include <crypto/aes-cbc-macs.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>
#include <crypto/utils.h>
@ -490,46 +490,21 @@ void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
* @sig: signature value generated for client request packet
*
*/
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig)
void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig)
{
struct ksmbd_crypto_ctx *ctx;
int rc, i;
struct aes_cmac_key cmac_key;
struct aes_cmac_ctx cmac_ctx;
int i;
ctx = ksmbd_crypto_ctx_find_cmacaes();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc cmac\n");
return -ENOMEM;
}
/* This cannot fail, since we always pass a valid key length. */
static_assert(SMB2_CMACAES_SIZE == AES_KEYSIZE_128);
aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
rc = crypto_shash_setkey(CRYPTO_CMACAES_TFM(ctx),
key,
SMB2_CMACAES_SIZE);
if (rc)
goto out;
rc = crypto_shash_init(CRYPTO_CMACAES(ctx));
if (rc) {
ksmbd_debug(AUTH, "cmaces init error %d\n", rc);
goto out;
}
for (i = 0; i < n_vec; i++) {
rc = crypto_shash_update(CRYPTO_CMACAES(ctx),
iov[i].iov_base,
iov[i].iov_len);
if (rc) {
ksmbd_debug(AUTH, "cmaces update error %d\n", rc);
goto out;
}
}
rc = crypto_shash_final(CRYPTO_CMACAES(ctx), sig);
if (rc)
ksmbd_debug(AUTH, "cmaces generation error %d\n", rc);
out:
ksmbd_release_crypto_ctx(ctx);
return rc;
aes_cmac_init(&cmac_ctx, &cmac_key);
for (i = 0; i < n_vec; i++)
aes_cmac_update(&cmac_ctx, iov[i].iov_base, iov[i].iov_len);
aes_cmac_final(&cmac_ctx, sig);
}
struct derivation {

View File

@ -54,8 +54,8 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
int in_len, char *out_blob, int *out_len);
void ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
void ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
int n_vec, char *sig);
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
struct ksmbd_conn *conn);
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,

View File

@ -98,6 +98,15 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
kfree(conn->preauth_info);
kfree(conn->mechToken);
if (atomic_dec_and_test(&conn->refcnt)) {
/*
* async_ida is embedded in struct ksmbd_conn, so pair
* ida_destroy() with the final kfree() rather than with
* the unconditional field teardown above. This keeps
* the IDA valid for the entire lifetime of the struct,
* even while other refcount holders (oplock / vfs
* durable handles) still reference the connection.
*/
ida_destroy(&conn->async_ida);
conn->transport->ops->free_transport(conn->transport);
kfree(conn);
}
@ -237,7 +246,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
{
struct ksmbd_conn *conn;
int rc, retry_count = 0, max_timeout = 120;
int rcount = 1, bkt;
int rcount, bkt;
retry_idle:
if (retry_count >= max_timeout)
@ -246,8 +255,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)
down_read(&conn_list_lock);
hash_for_each(conn_list, bkt, conn, hlist) {
if (conn->binding || xa_load(&conn->sessions, sess_id)) {
if (conn == curr_conn)
rcount = 2;
rcount = (conn == curr_conn) ? 2 : 1;
if (atomic_read(&conn->req_running) >= rcount) {
rc = wait_event_timeout(conn->req_running_q,
atomic_read(&conn->req_running) < rcount,

View File

@ -28,14 +28,6 @@ static inline void free_aead(struct crypto_aead *aead)
crypto_free_aead(aead);
}
static void free_shash(struct shash_desc *shash)
{
if (shash) {
crypto_free_shash(shash->tfm);
kfree(shash);
}
}
static struct crypto_aead *alloc_aead(int id)
{
struct crypto_aead *tfm = NULL;
@ -60,37 +52,10 @@ static struct crypto_aead *alloc_aead(int id)
return tfm;
}
static struct shash_desc *alloc_shash_desc(int id)
{
struct crypto_shash *tfm = NULL;
struct shash_desc *shash;
switch (id) {
case CRYPTO_SHASH_CMACAES:
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
break;
default:
return NULL;
}
if (IS_ERR(tfm))
return NULL;
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
KSMBD_DEFAULT_GFP);
if (!shash)
crypto_free_shash(tfm);
else
shash->tfm = tfm;
return shash;
}
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
{
int i;
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
free_shash(ctx->desc[i]);
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
free_aead(ctx->ccmaes[i]);
kfree(ctx);
@ -153,29 +118,6 @@ void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
ctx_free(ctx);
}
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;
if (id >= CRYPTO_SHASH_MAX)
return NULL;
ctx = ksmbd_find_crypto_ctx();
if (ctx->desc[id])
return ctx;
ctx->desc[id] = alloc_shash_desc(id);
if (ctx->desc[id])
return ctx;
ksmbd_release_crypto_ctx(ctx);
return NULL;
}
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
{
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
}
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
{
struct ksmbd_crypto_ctx *ctx;

View File

@ -6,14 +6,8 @@
#ifndef __CRYPTO_CTX_H__
#define __CRYPTO_CTX_H__
#include <crypto/hash.h>
#include <crypto/aead.h>
enum {
CRYPTO_SHASH_CMACAES = 0,
CRYPTO_SHASH_MAX,
};
enum {
CRYPTO_AEAD_AES_GCM = 16,
CRYPTO_AEAD_AES_CCM,
@ -23,19 +17,13 @@ enum {
struct ksmbd_crypto_ctx {
struct list_head list;
struct shash_desc *desc[CRYPTO_SHASH_MAX];
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
};
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
void ksmbd_crypto_destroy(void);

View File

@ -56,12 +56,6 @@ struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
goto err_free;
if (resp_ext) {
if (resp_ext->ngroups > NGROUPS_MAX) {
pr_err("ngroups(%u) from login response exceeds max groups(%d)\n",
resp_ext->ngroups, NGROUPS_MAX);
goto err_free;
}
user->sgid = kmemdup(resp_ext->____payload,
resp_ext->ngroups * sizeof(gid_t),
KSMBD_DEFAULT_GFP);

View File

@ -391,6 +391,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
free_channel_list(sess);
kfree(sess->Preauth_HashValue);
ksmbd_release_id(&session_ida, sess->id);
ida_destroy(&sess->tree_conn_ida);
kfree(sess);
}
@ -547,8 +548,13 @@ struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
struct ksmbd_session *sess;
sess = ksmbd_session_lookup(conn, id);
if (!sess && conn->binding)
if (!sess && conn->binding) {
sess = ksmbd_session_lookup_slowpath(id);
if (sess && !xa_load(&sess->ksmbd_chann_list, (long)conn)) {
ksmbd_user_session_put(sess);
sess = NULL;
}
}
if (sess && sess->state != SMB2_SESSION_VALID) {
ksmbd_user_session_put(sess);
sess = NULL;
@ -665,6 +671,8 @@ static struct ksmbd_session *__session_create(int protocol)
if (!sess)
return NULL;
ida_init(&sess->tree_conn_ida);
if (ksmbd_init_file_table(&sess->file_table))
goto error;
@ -684,8 +692,6 @@ static struct ksmbd_session *__session_create(int protocol)
if (ret)
goto error;
ida_init(&sess->tree_conn_ida);
down_write(&sessions_table_lock);
hash_add(sessions_table, &sess->hlist, sess->id);
up_write(&sessions_table_lock);

View File

@ -631,7 +631,6 @@ MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: nls");
MODULE_SOFTDEP("pre: aes");
MODULE_SOFTDEP("pre: cmac");
MODULE_SOFTDEP("pre: aead2");
MODULE_SOFTDEP("pre: ccm");
MODULE_SOFTDEP("pre: gcm");

View File

@ -2844,6 +2844,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
dh_info->reconnected = true;
goto out;
}
ksmbd_put_durable_fd(dh_info->fp);
dh_info->fp = NULL;
}
if ((lc && (lc->req_state & SMB2_LEASE_HANDLE_CACHING_LE)) ||
@ -3014,29 +3016,23 @@ int smb2_open(struct ksmbd_work *work)
if (dh_info.reconnected == true) {
rc = smb2_check_durable_oplock(conn, share, dh_info.fp,
lc, sess->user, name);
if (rc) {
ksmbd_put_durable_fd(dh_info.fp);
if (rc)
goto err_out2;
}
rc = ksmbd_reopen_durable_fd(work, dh_info.fp);
if (rc) {
ksmbd_put_durable_fd(dh_info.fp);
if (rc)
goto err_out2;
}
fp = dh_info.fp;
if (ksmbd_override_fsids(work)) {
rc = -ENOMEM;
ksmbd_put_durable_fd(dh_info.fp);
goto err_out2;
}
file_info = FILE_OPENED;
rc = ksmbd_vfs_getattr(&fp->filp->f_path, &stat);
ksmbd_put_durable_fd(fp);
if (rc)
goto err_out2;
@ -3061,7 +3057,7 @@ int smb2_open(struct ksmbd_work *work)
} else {
if (req->CreateOptions & FILE_SEQUENTIAL_ONLY_LE &&
req->CreateOptions & FILE_RANDOM_ACCESS_LE)
req->CreateOptions = ~(FILE_SEQUENTIAL_ONLY_LE);
req->CreateOptions &= ~FILE_SEQUENTIAL_ONLY_LE;
if (req->CreateOptions &
(FILE_OPEN_BY_FILE_ID_LE | CREATE_TREE_CONNECTION |
@ -3075,7 +3071,7 @@ int smb2_open(struct ksmbd_work *work)
rc = -EINVAL;
goto err_out2;
} else if (req->CreateOptions & FILE_NO_COMPRESSION_LE) {
req->CreateOptions = ~(FILE_NO_COMPRESSION_LE);
req->CreateOptions &= ~FILE_NO_COMPRESSION_LE;
}
}
}
@ -3806,6 +3802,9 @@ int smb2_open(struct ksmbd_work *work)
ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
}
if (dh_info.reconnected)
ksmbd_put_durable_fd(dh_info.fp);
kfree(name);
kfree(lc);
@ -4821,6 +4820,8 @@ static int smb2_get_ea(struct ksmbd_work *work, struct ksmbd_file *fp,
/* align next xattr entry at 4 byte bundary */
alignment_bytes = ((next_offset + 3) & ~3) - next_offset;
if (alignment_bytes) {
if (buf_free_len < alignment_bytes)
break;
memset(ptr, '\0', alignment_bytes);
ptr += alignment_bytes;
next_offset += alignment_bytes;
@ -5745,20 +5746,8 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
ksmbd_debug(SMB, "Unsupported addition info: 0x%x)\n",
addition_info);
pntsd = kzalloc(ALIGN(sizeof(struct smb_ntsd), 8),
KSMBD_DEFAULT_GFP);
if (!pntsd)
return -ENOMEM;
pntsd->revision = cpu_to_le16(1);
pntsd->type = cpu_to_le16(SELF_RELATIVE | DACL_PROTECTED);
pntsd->osidoffset = 0;
pntsd->gsidoffset = 0;
pntsd->sacloffset = 0;
pntsd->dacloffset = 0;
secdesclen = sizeof(struct smb_ntsd);
goto iov_pin;
rsp->hdr.Status = STATUS_NOT_SUPPORTED;
return -EINVAL;
}
if (work->next_smb2_rcv_hdr_off) {
@ -5825,7 +5814,6 @@ static int smb2_get_info_sec(struct ksmbd_work *work,
if (rc)
goto err_out;
iov_pin:
rsp->OutputBufferLength = cpu_to_le32(secdesclen);
rc = buffer_check_err(le32_to_cpu(req->OutputBufferLength),
rsp, work->response_buf);
@ -7492,7 +7480,12 @@ int smb2_lock(struct ksmbd_work *work)
lock_ele = req->locks;
ksmbd_debug(SMB, "lock count is %d\n", lock_count);
if (!lock_count) {
/*
* Cap lock_count at 64. The MS-SMB2 spec defines Open.LockSequenceArray
* as exactly 64 entries so 64 is the intended ceiling. No real workload
* comes close to this in a single request.
*/
if (!lock_count || lock_count > 64) {
err = -EINVAL;
goto out2;
}
@ -9067,8 +9060,7 @@ int smb3_check_sign_req(struct ksmbd_work *work)
iov[0].iov_base = (char *)&hdr->ProtocolId;
iov[0].iov_len = len;
if (ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature))
return 0;
ksmbd_sign_smb3_pdu(conn, signing_key, iov, 1, signature);
if (crypto_memneq(signature, signature_req, SMB2_SIGNATURE_SIZE)) {
pr_err("bad smb2 signature\n");
@ -9119,9 +9111,8 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
iov = &work->iov[work->iov_idx];
}
if (!ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec,
signature))
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
ksmbd_sign_smb3_pdu(conn, signing_key, iov, n_vec, signature);
memcpy(hdr->Signature, signature, SMB2_SIGNATURE_SIZE);
}
/**

View File

@ -596,6 +596,7 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
struct smb_sid *sid;
struct smb_ace *ntace;
int i, j;
u16 ace_sz;
if (!fattr->cf_acls)
goto posix_default_acl;
@ -640,8 +641,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
flags = 0x03;
ntace = (struct smb_ace *)((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, flags,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size))
break;
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
@ -650,8 +653,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
if (S_ISDIR(fattr->cf_mode) &&
(pace->e_tag == ACL_USER || pace->e_tag == ACL_GROUP)) {
ntace = (struct smb_ace *)((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED,
0x03, pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size))
break;
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
@ -691,8 +696,10 @@ static void set_posix_acl_entries_dacl(struct mnt_idmap *idmap,
}
ntace = (struct smb_ace *)((char *)pndace + *size);
*size += fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
ace_sz = fill_ace_for_sid(ntace, sid, ACCESS_ALLOWED, 0x0b,
pace->e_perm, 0777);
if (check_add_overflow(*size, ace_sz, size))
break;
(*num_aces)++;
if (pace->e_tag == ACL_USER)
ntace->access_req |=
@ -728,7 +735,8 @@ static void set_ntacl_dacl(struct mnt_idmap *idmap,
break;
memcpy((char *)pndace + size, ntace, nt_ace_size);
size += nt_ace_size;
if (check_add_overflow(size, nt_ace_size, &size))
break;
aces_size -= nt_ace_size;
ntace = (struct smb_ace *)((char *)ntace + nt_ace_size);
num_aces++;
@ -1106,8 +1114,24 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
goto free_parent_pntsd;
}
aces_base = kmalloc(sizeof(struct smb_ace) * num_aces * 2,
KSMBD_DEFAULT_GFP);
aces_size = pdacl_size - sizeof(struct smb_acl);
/*
* Validate num_aces against the DACL payload before allocating.
* Each ACE must be at least as large as its fixed-size header
* (up to the SID base), so num_aces cannot exceed the payload
* divided by the minimum ACE size. This mirrors the existing
* check in parse_dacl().
*/
if (num_aces > aces_size / (offsetof(struct smb_ace, sid) +
offsetof(struct smb_sid, sub_auth) +
sizeof(__le16))) {
rc = -EINVAL;
goto free_parent_pntsd;
}
aces_base = kmalloc_array(num_aces * 2, sizeof(struct smb_ace),
KSMBD_DEFAULT_GFP);
if (!aces_base) {
rc = -ENOMEM;
goto free_parent_pntsd;
@ -1116,7 +1140,6 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
aces = (struct smb_ace *)aces_base;
parent_aces = (struct smb_ace *)((char *)parent_pdacl +
sizeof(struct smb_acl));
aces_size = acl_len - sizeof(struct smb_acl);
if (pntsd_type & DACL_AUTO_INHERITED)
inherited_flags = INHERITED_ACE;
@ -1124,11 +1147,14 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
for (i = 0; i < num_aces; i++) {
int pace_size;
if (offsetof(struct smb_ace, access_req) > aces_size)
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
pace_size = le16_to_cpu(parent_aces->size);
if (pace_size > aces_size)
if (pace_size > aces_size ||
pace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
aces_size -= pace_size;
@ -1342,10 +1368,13 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (offsetof(struct smb_ace, access_req) > aces_size)
if (offsetof(struct smb_ace, sid) +
aces_size < CIFS_SID_BASE_SIZE)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size)
if (ace_size > aces_size ||
ace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
aces_size -= ace_size;
granted |= le32_to_cpu(ace->access_req);
@ -1360,13 +1389,19 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
aces_size = acl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (offsetof(struct smb_ace, access_req) > aces_size)
if (offsetof(struct smb_ace, sid) +
aces_size < CIFS_SID_BASE_SIZE)
break;
ace_size = le16_to_cpu(ace->size);
if (ace_size > aces_size)
if (ace_size > aces_size ||
ace_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
break;
aces_size -= ace_size;
if (ace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES)
break;
if (!compare_sids(&sid, &ace->sid) ||
!compare_sids(&sid_unix_NFS_mode, &ace->sid)) {
found = 1;

View File

@ -13,6 +13,7 @@
#include <net/genetlink.h>
#include <linux/socket.h>
#include <linux/workqueue.h>
#include <linux/overflow.h>
#include "vfs_cache.h"
#include "transport_ipc.h"
@ -496,7 +497,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_rpc_command *resp = entry->response;
msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
if (check_add_overflow(sizeof(struct ksmbd_rpc_command),
resp->payload_sz, &msg_sz))
return -EINVAL;
break;
}
case KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST:
@ -515,8 +518,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
if (resp->payload_sz < resp->veto_list_sz)
return -EINVAL;
msg_sz = sizeof(struct ksmbd_share_config_response) +
resp->payload_sz;
if (check_add_overflow(sizeof(struct ksmbd_share_config_response),
resp->payload_sz, &msg_sz))
return -EINVAL;
}
break;
}
@ -525,6 +529,12 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
struct ksmbd_login_response_ext *resp = entry->response;
if (resp->ngroups) {
if (resp->ngroups < 0 ||
resp->ngroups > NGROUPS_MAX) {
pr_err("ngroups(%d) from login response exceeds max groups(%d)\n",
resp->ngroups, NGROUPS_MAX);
return -EINVAL;
}
msg_sz = sizeof(struct ksmbd_login_response_ext) +
resp->ngroups * sizeof(gid_t);
}

View File

@ -18,7 +18,7 @@
#include "smb_common.h"
#include "../common/smb2status.h"
#include "transport_rdma.h"
#include "../common/smbdirect/smbdirect_public.h"
#include "../smbdirect/public.h"
#define SMB_DIRECT_PORT_IWARP 5445

View File

@ -25,6 +25,6 @@ static inline void init_smbd_max_io_size(unsigned int sz) { }
static inline unsigned int get_smbd_max_read_write_size(struct ksmbd_transport *kt) { return 0; }
#endif
#include "../common/smbdirect/smbdirect.h"
#include "../smbdirect/smbdirect.h"
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */

View File

@ -183,6 +183,8 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
t = alloc_transport(client_sk);
if (!t) {
sock_release(client_sk);
if (server_conf.max_connections)
atomic_dec(&active_num_conn);
return -ENOMEM;
}
@ -279,7 +281,7 @@ static int ksmbd_kthread_fn(void *p)
skip_max_ip_conns_limit:
if (server_conf.max_connections &&
atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
atomic_inc_return(&active_num_conn) > server_conf.max_connections) {
pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
atomic_read(&active_num_conn));
atomic_dec(&active_num_conn);

View File

@ -2,7 +2,7 @@
#
# smbdirect configuration
config SMB_COMMON_SMBDIRECT
config SMBDIRECT
def_tristate n
depends on INFINIBAND && INFINIBAND_ADDR_TRANS
depends on m || INFINIBAND=y

18
fs/smb/smbdirect/Makefile Normal file
View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for smbdirect support
#
obj-$(CONFIG_SMBDIRECT) += smbdirect.o
smbdirect-y := \
socket.o \
connection.o \
mr.o \
rw.o \
debug.o \
connect.o \
listen.o \
accept.o \
devices.o \
main.o

View File

@ -5,9 +5,9 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
#include <net/sock.h>
#include "../../common/smb2status.h"
#include "../common/smb2status.h"
static int smbdirect_accept_rdma_event_handler(struct rdma_cm_id *id,
struct rdma_cm_event *event);

View File

@ -3,8 +3,8 @@
* Copyright (c) 2012,2016,2017,2025 Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "../../common/smb2status.h"
#include "internal.h"
#include "../common/smb2status.h"
static int smbdirect_connect_setup_connection(struct smbdirect_socket *sc);
static int smbdirect_connect_resolve_addr(struct smbdirect_socket *sc,

View File

@ -4,7 +4,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
#include <linux/folio_queue.h>
struct smbdirect_map_sges {

View File

@ -4,7 +4,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
#include <linux/seq_file.h>
void smbdirect_connection_legacy_debug_proc_show(struct smbdirect_socket *sc,

View File

@ -5,7 +5,7 @@
* Copyright (c) 2025 Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
static u8 smbdirect_ib_device_rdma_capable_node_type(struct ib_device *ib_dev)
{

View File

@ -9,8 +9,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "smbdirect.h"
#include "smbdirect_pdu.h"
#include "smbdirect_public.h"
#include "pdu.h"
#include "public.h"
#include <linux/mutex.h>
@ -34,7 +34,7 @@ struct smbdirect_module_state {
extern struct smbdirect_module_state smbdirect_globals;
#include "smbdirect_socket.h"
#include "socket.h"
struct smbdirect_device {
struct list_head list;

View File

@ -5,7 +5,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
static int smbdirect_listen_rdma_event_handler(struct rdma_cm_id *id,
struct rdma_cm_event *event);

View File

@ -3,7 +3,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
#include <linux/module.h>
struct smbdirect_module_state smbdirect_globals = {

View File

@ -4,7 +4,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
/*
* Allocate MRs used for RDMA read/write

View File

@ -5,7 +5,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
static int smbdirect_connection_wait_for_rw_credits(struct smbdirect_socket *sc,
int credits)

View File

@ -4,7 +4,7 @@
* Copyright (c) 2025, Stefan Metzmacher
*/
#include "smbdirect_internal.h"
#include "internal.h"
bool smbdirect_frwr_is_supported(const struct ib_device_attr *attrs)
{