Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions drivers/net/bonding/bond_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5155,18 +5155,22 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
struct net_device *bond_dev)
{
struct bonding *bond = netdev_priv(bond_dev);
struct slave *slave = NULL;
struct list_head *iter;
struct bond_up_slave *slaves;
bool xmit_suc = false;
bool skb_used = false;
int slaves_count, i;

bond_for_each_slave_rcu(bond, slave, iter) {
slaves = rcu_dereference(bond->all_slaves);

slaves_count = slaves ? READ_ONCE(slaves->count) : 0;
for (i = 0; i < slaves_count; i++) {
struct slave *slave = slaves->arr[i];
struct sk_buff *skb2;

if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
continue;

if (bond_is_last_slave(bond, slave)) {
if (i + 1 == slaves_count) {
skb2 = skb;
skb_used = true;
} else {
Expand Down
131 changes: 118 additions & 13 deletions fs/cifs/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -753,14 +753,86 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
}
#endif

static int validate_dacl(struct cifs_acl *pdacl, char *end_of_acl)
{
int i, ace_hdr_size, ace_size, min_ace_size;
u16 dacl_size;
u32 num_aces;
char *acl_base, *end_of_dacl;
struct cifs_ace *pace;

if (!pdacl)
return 0;

if (end_of_acl < (char *)pdacl + sizeof(struct cifs_acl)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

dacl_size = le16_to_cpu(pdacl->size);
if (dacl_size < sizeof(struct cifs_acl) ||
end_of_acl < (char *)pdacl + dacl_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

num_aces = le32_to_cpu(pdacl->num_aces);
if (!num_aces)
return 0;

ace_hdr_size = offsetof(struct cifs_ace, sid) +
offsetof(struct cifs_sid, sub_auth);
min_ace_size = ace_hdr_size + sizeof(__le32);
if (num_aces > (dacl_size - sizeof(struct cifs_acl)) / min_ace_size) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
return -EINVAL;
}

end_of_dacl = (char *)pdacl + dacl_size;
acl_base = (char *)pdacl;
ace_size = sizeof(struct cifs_acl);

for (i = 0; i < num_aces; ++i) {
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

pace = (struct cifs_ace *)(acl_base + ace_size);
acl_base = (char *)pace;

if (end_of_dacl - acl_base < ace_hdr_size ||
pace->sid.num_subauth == 0 ||
pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
if (end_of_dacl - acl_base < ace_size ||
le16_to_cpu(pace->size) < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}

ace_size = le16_to_cpu(pace->size);
if (end_of_dacl - acl_base < ace_size) {
cifs_dbg(VFS, "ACL too small to parse ACE\n");
return -EINVAL;
}
}

return 0;
}

static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
struct cifs_fattr *fattr, bool mode_from_special_sid)
{
int i;
int num_aces = 0;
int acl_size;
char *acl_base;
char *acl_base, *end_of_dacl;
struct cifs_ace **ppace;

/* BB need to add parm so we can store the SID BB */
Expand All @@ -772,11 +844,8 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
return;
}

/* validate that we do not go past end of acl */
if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
cifs_dbg(VFS, "ACL too small to parse DACL\n");
if (validate_dacl(pdacl, end_of_acl))
return;
}

cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
Expand All @@ -787,6 +856,7 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
user/group/other have no permissions */
fattr->cf_mode &= ~(0777);

end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size);
acl_base = (char *)pdacl;
acl_size = sizeof(struct cifs_acl);

Expand All @@ -804,9 +874,10 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
for (i = 0; i < num_aces; ++i) {
ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
#ifdef CONFIG_CIFS_DEBUG2
dump_ace(ppace[i], end_of_acl);
dump_ace(ppace[i], end_of_dacl);
#endif
if (mode_from_special_sid &&
ppace[i]->sid.num_subauth >= 3 &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
Expand Down Expand Up @@ -1183,6 +1254,17 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
return 0;
}

static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
{
if (acl_len < sizeof(struct cifs_acl))
return false;

if (dacloffset < sizeof(struct cifs_ntsd))
return false;

return dacloffset <= acl_len - sizeof(struct cifs_acl);
}


/* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
Expand All @@ -1203,7 +1285,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
Expand Down Expand Up @@ -1234,11 +1315,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
return rc;
}

if (dacloffset)
if (dacloffset) {
if (!dacl_offset_valid(acl_len, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}

dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr, get_mode_from_special_sid);
else
} else {
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
}

return rc;
}
Expand All @@ -1261,11 +1349,15 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,

dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
cifs_dbg(VFS, "Server returned illegal ACL size\n");
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}

dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, end_of_acl);
if (rc)
return rc;
}

owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
Expand Down Expand Up @@ -1626,7 +1718,19 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen = sizeof(struct cifs_ntsd) + (sizeof(struct cifs_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
rc = -EINVAL;
goto id_mode_to_cifs_acl_exit;
}

dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
if (rc) {
kfree(pntsd);
cifs_put_tlink(tlink);
return rc;
}
if (mode_from_sid)
nsecdesclen +=
le32_to_cpu(dacl_ptr->num_aces) * sizeof(struct cifs_ace);
Expand All @@ -1642,7 +1746,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
* descriptor parameters, and security descriptor itself
*/
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
Expand All @@ -1662,6 +1766,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);

kfree(pnntsd);
Expand Down
Loading