From b2faddc13112489f8f11eb40b9456db8c1b58362 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Sun, 22 Mar 2026 11:04:06 -0700 Subject: [PATCH 1/8] Smack: Fix error in capability bypass A bug in smack_inode_xattr_skipcap() was introduced in the inode capability handling. The strncmp guard at the top of the function is coded backwards, resulting in consistently incorrect results. Correct the check, and the code functions as it should. The error manifests as requiring CAP_SYS_ADMIN as well as CAP_MAC_ADMIN to change an inode's MAC attributes. Fixes: 61df7b828204 ("lsm: fixup the inode xattr capability handling") Reported-by: Bumjin Im Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 98af9d7b9434..f4ef840b203e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1312,7 +1312,7 @@ static int smack_inode_getattr(const struct path *path) */ static int smack_inode_xattr_skipcap(const char *name) { - if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX))) + if (strncmp(name, XATTR_SMACK_SUFFIX, strlen(XATTR_SMACK_SUFFIX)) == 0) return 0; if (strcmp(name, XATTR_NAME_SMACK) == 0 || From b78fede1c69a090d377bf80417ce1f7f7f314534 Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Fri, 20 Mar 2026 14:31:57 +0300 Subject: [PATCH 2/8] smack: simplify write handlers of sysfs entries Use the convenient 'kstrto{u,s}32_from_user()' to simplify write handlers of /smack/{doi,direct,mapped,logging,ptrace} sysfs entries. Signed-off-by: Dmitry Antipov Signed-off-by: Casey Schaufler --- security/smack/smackfs.c | 81 +++++++++++----------------------------- 1 file changed, 22 insertions(+), 59 deletions(-) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 6e62dcb36f74..f60d5469043e 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -1598,24 +1598,17 @@ static ssize_t smk_read_doi(struct file *filp, char __user *buf, static ssize_t smk_write_doi(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char temp[80]; - unsigned long u; + int ret; + u32 u; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (count >= sizeof(temp) || count == 0) - return -EINVAL; + ret = kstrtou32_from_user(buf, count, 10, &u); + if (unlikely(ret)) + return ret; - if (copy_from_user(temp, buf, count) != 0) - return -EFAULT; - - temp[count] = '\0'; - - if (kstrtoul(temp, 10, &u)) - return -EINVAL; - - if (u == CIPSO_V4_DOI_UNKNOWN || u > U32_MAX) + if (u == CIPSO_V4_DOI_UNKNOWN) return -EINVAL; return smk_cipso_doi(u, GFP_KERNEL) ? : count; @@ -1664,22 +1657,14 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct smack_known *skp; - char temp[80]; - int i; + int i, ret; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (count >= sizeof(temp) || count == 0) - return -EINVAL; - - if (copy_from_user(temp, buf, count) != 0) - return -EFAULT; - - temp[count] = '\0'; - - if (sscanf(temp, "%d", &i) != 1) - return -EINVAL; + ret = kstrtos32_from_user(buf, count, 10, &i); + if (unlikely(ret)) + return ret; /* * Don't do anything if the value hasn't actually changed. @@ -1742,22 +1727,14 @@ static ssize_t smk_write_mapped(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct smack_known *skp; - char temp[80]; - int i; + int i, ret; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (count >= sizeof(temp) || count == 0) - return -EINVAL; - - if (copy_from_user(temp, buf, count) != 0) - return -EFAULT; - - temp[count] = '\0'; - - if (sscanf(temp, "%d", &i) != 1) - return -EINVAL; + ret = kstrtos32_from_user(buf, count, 10, &i); + if (unlikely(ret)) + return ret; /* * Don't do anything if the value hasn't actually changed. @@ -2179,22 +2156,15 @@ static ssize_t smk_read_logging(struct file *filp, char __user *buf, static ssize_t smk_write_logging(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char temp[32]; - int i; + int i, ret; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (count >= sizeof(temp) || count == 0) - return -EINVAL; + ret = kstrtos32_from_user(buf, count, 10, &i); + if (unlikely(ret)) + return ret; - if (copy_from_user(temp, buf, count) != 0) - return -EFAULT; - - temp[count] = '\0'; - - if (sscanf(temp, "%d", &i) != 1) - return -EINVAL; if (i < 0 || i > 3) return -EINVAL; log_policy = i; @@ -2838,22 +2808,15 @@ static ssize_t smk_read_ptrace(struct file *filp, char __user *buf, static ssize_t smk_write_ptrace(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char temp[32]; - int i; + int i, ret; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - if (*ppos != 0 || count >= sizeof(temp) || count == 0) - return -EINVAL; + ret = kstrtos32_from_user(buf, count, 10, &i); + if (unlikely(ret)) + return ret; - if (copy_from_user(temp, buf, count) != 0) - return -EFAULT; - - temp[count] = '\0'; - - if (sscanf(temp, "%d", &i) != 1) - return -EINVAL; if (i < SMACK_PTRACE_DEFAULT || i > SMACK_PTRACE_MAX) return -EINVAL; smack_ptrace_rule = i; From 3cef22655c9d3d0023ecd6d4cd1620e41dd0af75 Mon Sep 17 00:00:00 2001 From: Qingshuang Fu Date: Tue, 26 May 2026 09:38:34 +0800 Subject: [PATCH 3/8] security: smack: fix spelling mistake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix misspelling: overriden → overridden Signed-off-by: Qingshuang Fu Changes since v1: - Split original single patch into two standalone patches, separate AppArmor and Smack changes for different maintainer trees. Signed-off-by: Casey Schaufler --- security/smack/smackfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index f60d5469043e..af34b22335d5 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -115,7 +115,7 @@ struct smack_known *smack_syslog_label; /* * Ptrace current rule * SMACK_PTRACE_DEFAULT regular smack ptrace rules (/proc based) - * SMACK_PTRACE_EXACT labels must match, but can be overriden with + * SMACK_PTRACE_EXACT labels must match, but can be overridden with * CAP_SYS_PTRACE * SMACK_PTRACE_DRACONIAN labels must match, CAP_SYS_PTRACE has no effect */ From fba3d32825f4bbc8e20f0cdc3b14df57965b8fe5 Mon Sep 17 00:00:00 2001 From: Konstantin Andreev Date: Mon, 11 May 2026 03:17:16 +0300 Subject: [PATCH 4/8] smack: fix incorrect task context in smack_msg_queue_msgrcv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The smack_msg_queue_msgrcv() function incorrectly checks the permissions of the 'current' task instead of the 'target' task. In the msgsnd() syscall path, if a receiver is already waiting, the pipelined_send() optimization is used to push the message directly to the receiver task: ipc/msg.c`pipelined_send(): ` smp_store_release(&msr->r_msg, msg) In this case, the 'sender' (current) task performs the check on behalf of the 'receiver' task (msr->r_tsk, passed as the 'target' parameter): ipc/msg.c`pipelined_send(): ` security_msg_queue_msgrcv(,, target := msr->r_tsk,,) However, smack_msg_queue_msgrcv() ignores the 'target' and checks 'current': smack_msg_queue_msgrcv(…) ` smk_curacc_msq(isp, MAY_READWRITE); // current task 'current' MAY satisfy smack_msg_queue_msgrcv r/w requirement, but 'target' (the receiver task) might NOT; as a result, an unauthorized receiver gets the message, violating MAC policy. Test: 1) create a sysv message queue with label “foo” 2) echo "bar foo r" >/smack/load2 3) msgrcv(,,,0,MSG_NOERROR) in "bar"-labeled task. The task is waiting for the messages ... 4) msgsnd() from a "foo"-labeled task: "bar"-labeled task gets the message. This patch fixes the issue by checking permission on the 'target' task instead of 'current'. (2008-02-04, Casey Schaufler) Fixes: e114e473771c ("Smack: Simplified Mandatory Access Control Kernel") Signed-off-by: Konstantin Andreev Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 65 +++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 18 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f4ef840b203e..3146fa83c2f1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -130,12 +130,13 @@ static int smk_bu_note(char *note, struct smack_known *sskp, #define smk_bu_note(note, sskp, oskp, mode, RC) (RC) #endif -#ifdef CONFIG_SECURITY_SMACK_BRINGUP -static int smk_bu_current(char *note, struct smack_known *oskp, - int mode, int rc) +static int +smk_bu_tsk_to_obj(struct task_struct *tsk, const struct task_smack *tsp, + char *note, struct smack_known *oskp, int mode, int rc) { - struct task_smack *tsp = smack_cred(current_cred()); +#ifdef CONFIG_SECURITY_SMACK_BRINGUP char acc[SMK_NUM_ACCESS_TYPE + 1]; + char comm[TASK_COMM_LEN]; if (rc <= 0) return rc; @@ -143,14 +144,22 @@ static int smk_bu_current(char *note, struct smack_known *oskp, rc = 0; smk_bu_mode(mode, acc); + pr_info("Smack %s: (%s %s %s) %s %s\n", smk_bu_mess[rc], - tsp->smk_task->smk_known, oskp->smk_known, - acc, current->comm, note); + smk_of_task(tsp)->smk_known, oskp->smk_known, + acc, get_task_comm(comm, tsk), note); return 0; -} #else -#define smk_bu_current(note, oskp, mode, RC) (RC) + return rc; #endif +} + +static int smk_bu_current(char *note, struct smack_known *oskp, + int mode, int rc) +{ + return smk_bu_tsk_to_obj(current, smack_cred(current_cred()), + note, oskp, mode, rc); +} #ifdef CONFIG_SECURITY_SMACK_BRINGUP static int smk_bu_task(struct task_struct *otp, int mode, int rc) @@ -3353,14 +3362,20 @@ static int smack_sem_semop(struct kern_ipc_perm *isp, struct sembuf *sops, } /** - * smk_curacc_msq : helper to check if current has access on msq - * @isp : the msq + * smk_tskacc_msq : helper to check if tsk has access on msq + * @tsk: the task that requests access + * @isp : the sysv msg queue permissions * @access : access requested * - * return 0 if current has access, error otherwise + * return 0 if tsk has access, error otherwise */ -static int smk_curacc_msq(struct kern_ipc_perm *isp, int access) +static int +smk_tskacc_msq(struct task_struct *tsk, struct kern_ipc_perm *isp, int access) { + const bool tsk_is_current = (tsk == current); + const struct cred * const tsk_cred = + (tsk_is_current ? current_cred() : get_task_cred(tsk)); + struct task_smack * const tsp = smack_cred(tsk_cred); struct smack_known *msp = smack_of_ipc(isp); struct smk_audit_info ad; int rc; @@ -3369,11 +3384,25 @@ static int smk_curacc_msq(struct kern_ipc_perm *isp, int access) smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = isp->id; #endif - rc = smk_curacc(msp, access, &ad); - rc = smk_bu_current("msq", msp, access, rc); + rc = smk_tskacc(tsp, msp, access, &ad); + rc = smk_bu_tsk_to_obj(tsk, tsp, "msq", msp, access, rc); + if (!tsk_is_current) + put_cred(tsk_cred); return rc; } +/** + * smk_curacc_msq : helper to check if current has access on msq + * @isp : the sysv msg queue permissions + * @access : access requested + * + * return 0 if current has access, error otherwise + */ +static int smk_curacc_msq(struct kern_ipc_perm *isp, int access) +{ + return smk_tskacc_msq(current, isp, access); +} + /** * smack_msg_queue_associate - Smack access check for msg_queue * @isp: the object @@ -3441,21 +3470,21 @@ static int smack_msg_queue_msgsnd(struct kern_ipc_perm *isp, struct msg_msg *msg } /** - * smack_msg_queue_msgrcv - Smack access check for msg_queue + * smack_msg_queue_msgrcv - check it target has r/w access to msg_queue * @isp: the object * @msg: unused - * @target: unused + * @target: the task that msgrcv() from the queue * @type: unused * @mode: unused * - * Returns 0 if current has read and write access, error code otherwise + * Returns 0 if target has read and write access, error code otherwise */ static int smack_msg_queue_msgrcv(struct kern_ipc_perm *isp, struct msg_msg *msg, struct task_struct *target, long type, int mode) { - return smk_curacc_msq(isp, MAY_READWRITE); + return smk_tskacc_msq(target, isp, MAY_READWRITE); } /** From 0bcb3c5e7101c2df267aabc17b30939da18a2424 Mon Sep 17 00:00:00 2001 From: Konstantin Andreev Date: Mon, 11 May 2026 03:17:17 +0300 Subject: [PATCH 5/8] smack: show msgrcv() subject task in audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a task msgrcv()'es some message the SMACK audit log message looks like: fn=smk_tskacc_msq action=denied subject="bar" object="foo" requested=rw pid=456 comm="mrcv" ipc_key=2 fn=smk_tskacc_msq action=granted subject="bar" object="foo" requested=rw pid=519 comm="mrcv" ipc_key=2 where pid= is a pid of a “current” task which calls smk_tskacc_msq(). Usually, the caller of smk_tskacc_msq() is also a subject task which determines its own permission. In the example above the 'mrcv' process has label 'bar' and wants "rw" for label "foo". However, when sender task delivers message using ipc/msg.c`pipelined_send(): ` security_msg_queue_msgrcv(,, msr->r_tsk,,) ` smp_store_release(&msr->r_msg, msg) “current” task and “subject” task differ, and the “subject” task is missed from the audit message. This patch adds two fields, subj_pid and subj_comm, into the audit message: fn=smk_tskacc_msq action=granted subject="bar" object="foo" requested=rw subj_pid=564 subj_comm="mrcv" pid=577 comm="msnd" ipc_key=2 Signed-off-by: Konstantin Andreev Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_access.c | 9 +++++++++ security/smack/smack_lsm.c | 2 ++ 3 files changed, 12 insertions(+) diff --git a/security/smack/smack.h b/security/smack/smack.h index 9b9eb262fe33..551fcf2a1832 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -261,6 +261,7 @@ struct smack_audit_data { char *subject; char *object; char *request; + struct task_struct *subj_tsk; int result; }; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 350b88d582b3..fb85356266e5 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -331,6 +331,15 @@ static void smack_log_callback(struct audit_buffer *ab, void *a) audit_log_format(ab, " labels_differ"); else audit_log_format(ab, " requested=%s", sad->request); + + if (sad->subj_tsk) { + char comm[TASK_COMM_LEN]; + + audit_log_format(ab, " subj_pid=%d subj_comm=", + task_tgid_nr(sad->subj_tsk)); + audit_log_untrustedstring(ab, + get_task_comm(comm, sad->subj_tsk)); + } } /** diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 3146fa83c2f1..6f6ff9b20981 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3383,6 +3383,8 @@ smk_tskacc_msq(struct task_struct *tsk, struct kern_ipc_perm *isp, int access) #ifdef CONFIG_AUDIT smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_IPC); ad.a.u.ipc_id = isp->id; + if (!tsk_is_current) + ad.sad.subj_tsk = tsk; #endif rc = smk_tskacc(tsp, msp, access, &ad); rc = smk_bu_tsk_to_obj(tsk, tsp, "msq", msp, access, rc); From 577dc3b6a8cf200e6e27b2d9967cac14a1fed2f3 Mon Sep 17 00:00:00 2001 From: Konstantin Andreev Date: Mon, 25 May 2026 01:37:48 +0300 Subject: [PATCH 6/8] smack: deduplicate smackfs/{direct,mapped} file_operations The file_operations for smackfs/direct and smackfs/mapped are identical up to a textual replacement of "direct" with "mapped" This patch combines two instances of file_operations into one, handling both files. Fixes: f7112e6c9abf ("Smack: allow for significantly longer Smack labels v4") Signed-off-by: Konstantin Andreev Signed-off-by: Casey Schaufler --- security/smack/smack.h | 5 +- security/smack/smackfs.c | 135 ++++++++++++--------------------------- 2 files changed, 43 insertions(+), 97 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 551fcf2a1832..89fd4767c3e9 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -318,8 +318,9 @@ int smack_populate_secattr(struct smack_known *skp); * Shared data. */ extern int smack_enabled __initdata; -extern int smack_cipso_direct; -extern int smack_cipso_mapped; +extern int smack_cipso_auto_level[2]; +#define smack_cipso_direct (+smack_cipso_auto_level[0]) +#define smack_cipso_mapped (+smack_cipso_auto_level[1]) extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_syslog_label; #ifdef CONFIG_SECURITY_SMACK_BRINGUP diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index af34b22335d5..94c957184268 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -83,18 +83,27 @@ static DEFINE_MUTEX(smk_net6addr_lock); struct smack_known *smack_net_ambient; /* - * This is the level in a CIPSO header that indicates a + * Sensitivity levels for automatically created CIPSO labels. + * See smack_access.c`smack_populate_secattr() + * + * [0] "direct" labeling, label length < SMK_CIPSOLEN(24): * smack label is contained directly in the category set. * It can be reset via smackfs/direct - */ -int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; - -/* - * This is the level in a CIPSO header that indicates a + * + * [1] "mapped" labeling, label length >= SMK_CIPSOLEN(24): * secid is contained directly in the category set. * It can be reset via smackfs/mapped */ -int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; +int smack_cipso_auto_level[2] = { + SMACK_CIPSO_DIRECT_DEFAULT, + SMACK_CIPSO_MAPPED_DEFAULT, +}; + +static int +smk_cipso_auto_level_idx(const struct file *file) +{ + return (file_inode(file)->i_ino != SMK_DIRECT); +} #ifdef CONFIG_SECURITY_SMACK_BRINGUP /* @@ -1621,15 +1630,15 @@ static const struct file_operations smk_doi_ops = { }; /** - * smk_read_direct - read() for /smack/direct - * @filp: file pointer, not actually used + * smk_read_cipso_auto_level - read() for smackfs/direct and smackfs/mapped + * @filp: file pointer * @buf: where to put the result * @count: maximum to send along * @ppos: where to start * * Returns number of bytes read or error code, as appropriate */ -static ssize_t smk_read_direct(struct file *filp, char __user *buf, +static ssize_t smk_read_cipso_auto_level(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { char temp[80]; @@ -1638,26 +1647,28 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf, if (*ppos != 0) return 0; - sprintf(temp, "%d", smack_cipso_direct); + sprintf(temp, "%d", smack_cipso_auto_level[ + smk_cipso_auto_level_idx(filp)]); rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); return rc; } /** - * smk_write_direct - write() for /smack/direct - * @file: file pointer, not actually used + * smk_write_cipso_auto_level - write() for smackfs/direct and smackfs/mapped + * @filp: file pointer * @buf: where to get the data from * @count: bytes sent * @ppos: where to start * * Returns number of bytes written or error code, as appropriate */ -static ssize_t smk_write_direct(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t +smk_write_cipso_auto_level(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) { struct smack_known *skp; - int i, ret; + int i, ret, idx, old_lvl; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; @@ -1669,94 +1680,28 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf, /* * Don't do anything if the value hasn't actually changed. * If it is changing reset the level on entries that were - * set up to be direct when they were created. + * set up to be "auto" level when they were created. */ - if (smack_cipso_direct != i) { + idx = smk_cipso_auto_level_idx(filp); + old_lvl = smack_cipso_auto_level[idx]; + + if (old_lvl != i) { mutex_lock(&smack_known_lock); list_for_each_entry_rcu(skp, &smack_known_list, list) if (skp->smk_netlabel.attr.mls.lvl == - smack_cipso_direct) + old_lvl) skp->smk_netlabel.attr.mls.lvl = i; - smack_cipso_direct = i; + smack_cipso_auto_level[idx] = i; mutex_unlock(&smack_known_lock); } return count; } -static const struct file_operations smk_direct_ops = { - .read = smk_read_direct, - .write = smk_write_direct, - .llseek = default_llseek, -}; - -/** - * smk_read_mapped - read() for /smack/mapped - * @filp: file pointer, not actually used - * @buf: where to put the result - * @count: maximum to send along - * @ppos: where to start - * - * Returns number of bytes read or error code, as appropriate - */ -static ssize_t smk_read_mapped(struct file *filp, char __user *buf, - size_t count, loff_t *ppos) -{ - char temp[80]; - ssize_t rc; - - if (*ppos != 0) - return 0; - - sprintf(temp, "%d", smack_cipso_mapped); - rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); - - return rc; -} - -/** - * smk_write_mapped - write() for /smack/mapped - * @file: file pointer, not actually used - * @buf: where to get the data from - * @count: bytes sent - * @ppos: where to start - * - * Returns number of bytes written or error code, as appropriate - */ -static ssize_t smk_write_mapped(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct smack_known *skp; - int i, ret; - - if (!smack_privileged(CAP_MAC_ADMIN)) - return -EPERM; - - ret = kstrtos32_from_user(buf, count, 10, &i); - if (unlikely(ret)) - return ret; - - /* - * Don't do anything if the value hasn't actually changed. - * If it is changing reset the level on entries that were - * set up to be mapped when they were created. - */ - if (smack_cipso_mapped != i) { - mutex_lock(&smack_known_lock); - list_for_each_entry_rcu(skp, &smack_known_list, list) - if (skp->smk_netlabel.attr.mls.lvl == - smack_cipso_mapped) - skp->smk_netlabel.attr.mls.lvl = i; - smack_cipso_mapped = i; - mutex_unlock(&smack_known_lock); - } - - return count; -} - -static const struct file_operations smk_mapped_ops = { - .read = smk_read_mapped, - .write = smk_write_mapped, +static const struct file_operations +smk_cipso_auto_level_ops = { + .read = smk_read_cipso_auto_level, + .write = smk_write_cipso_auto_level, .llseek = default_llseek, }; @@ -2851,7 +2796,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc) [SMK_DOI] = { "doi", &smk_doi_ops, S_IRUGO|S_IWUSR}, [SMK_DIRECT] = { - "direct", &smk_direct_ops, S_IRUGO|S_IWUSR}, + "direct", &smk_cipso_auto_level_ops, 0644}, [SMK_AMBIENT] = { "ambient", &smk_ambient_ops, S_IRUGO|S_IWUSR}, [SMK_NET4ADDR] = { @@ -2867,7 +2812,7 @@ static int smk_fill_super(struct super_block *sb, struct fs_context *fc) [SMK_ACCESSES] = { "access", &smk_access_ops, S_IRUGO|S_IWUGO}, [SMK_MAPPED] = { - "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR}, + "mapped", &smk_cipso_auto_level_ops, 0644}, [SMK_LOAD2] = { "load2", &smk_load2_ops, S_IRUGO|S_IWUSR}, [SMK_LOAD_SELF2] = { From a7c44fd9f80e37763acf9cd3c87a58058d206427 Mon Sep 17 00:00:00 2001 From: Konstantin Andreev Date: Mon, 25 May 2026 01:37:49 +0300 Subject: [PATCH 7/8] smack: restrict smackfs/{direct,mapped} values to 0-255 Both smackfs/direct and smackfs/mapped incorrectly accept the full range of integer values. For example: # cd /sys/fs/smackfs/ # cat direct ; echo 250 # cat cipso2 @ 250/2 _ 250/2,4,5,6,7,8 * 250/3,5,7 ^ 250/2,4,5,6,7 ? 250/3,4,5,6,7,8 # echo -1234 >direct ; cat direct ; echo -1234 # cat cipso2 @ -1234/2 _ -1234/2,4,5,6,7,8 * -1234/3,5,7 ^ -1234/2,4,5,6,7 ? -1234/3,4,5,6,7,8 # I noticed two things regarding this: 1) sensitivity levels are truncated to 8 bits when labeling outgoing packets (0x2e = 46 for the -1234 example above) 2) the reverse process fails: incoming packets with sensitivity level 46 do not match these smackfs/cipso2 entries. Even observation (1) on its own warrants a fix. This patch restricts smackfs/direct and smackfs/mapped accepted values to the 0-255 range. Fixes: e114e473771c ("Smack: Simplified Mandatory Access Control Kernel") Signed-off-by: Konstantin Andreev Signed-off-by: Casey Schaufler --- security/smack/smack.h | 2 +- security/smack/smackfs.c | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/security/smack/smack.h b/security/smack/smack.h index 89fd4767c3e9..2a60a045b8cf 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -318,7 +318,7 @@ int smack_populate_secattr(struct smack_known *skp); * Shared data. */ extern int smack_enabled __initdata; -extern int smack_cipso_auto_level[2]; +extern u8 smack_cipso_auto_level[2]; #define smack_cipso_direct (+smack_cipso_auto_level[0]) #define smack_cipso_mapped (+smack_cipso_auto_level[1]) extern struct smack_known *smack_net_ambient; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 94c957184268..a72bc7fabea9 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -94,7 +94,7 @@ struct smack_known *smack_net_ambient; * secid is contained directly in the category set. * It can be reset via smackfs/mapped */ -int smack_cipso_auto_level[2] = { +u8 smack_cipso_auto_level[2] = { SMACK_CIPSO_DIRECT_DEFAULT, SMACK_CIPSO_MAPPED_DEFAULT, }; @@ -1641,17 +1641,15 @@ static const struct file_operations smk_doi_ops = { static ssize_t smk_read_cipso_auto_level(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { - char temp[80]; - ssize_t rc; + char temp[sizeof "255"]; + int n; if (*ppos != 0) return 0; - sprintf(temp, "%d", smack_cipso_auto_level[ - smk_cipso_auto_level_idx(filp)]); - rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); - - return rc; + n = sprintf(temp, "%u", (unsigned int)smack_cipso_auto_level[ + smk_cipso_auto_level_idx(filp)]); + return simple_read_from_buffer(buf, count, ppos, temp, n); } /** @@ -1667,13 +1665,16 @@ static ssize_t smk_write_cipso_auto_level(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { - struct smack_known *skp; - int i, ret, idx, old_lvl; + int ret, idx; + u8 i, old_lvl; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; - - ret = kstrtos32_from_user(buf, count, 10, &i); + /* + * draft-ietf-cipso-ipsecurity-01 (CIPSO 2.2), 3.4.2.4: + * "Sensitivity Level is 1 octet in length. Its value is from 0 to 255" + */ + ret = kstrtou8_from_user(buf, count, 10, &i); if (unlikely(ret)) return ret; @@ -1686,6 +1687,7 @@ smk_write_cipso_auto_level(struct file *filp, const char __user *buf, old_lvl = smack_cipso_auto_level[idx]; if (old_lvl != i) { + struct smack_known *skp; mutex_lock(&smack_known_lock); list_for_each_entry_rcu(skp, &smack_known_list, list) if (skp->smk_netlabel.attr.mls.lvl == From fedc88e38ce979a720cd2de042578cb5df3dc8de Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Thu, 6 Aug 2026 21:41:35 +0200 Subject: [PATCH 8/8] smack: fix cred UAF in smack_file_send_sigiotask() When inspecting the credentials of another task, objective credentials (->real_cred, accessed with __task_cred()) must always be used. Accessing ->cred on a non-current task is forbidden unless that task is being created or destroyed; a task is allowed to change its own ->cred pointer with no synchronization, and changing ->cred should only affect the current syscall. smack_file_send_sigiotask() was accessing both sets of credentials: First tsk->cred, then __task_cred(tsk). Fix it, always access the objective credentials here. I have tested that this bug can lead to a KASAN-reported UAF of struct cred in smack_file_send_sigiotask(), and that this fix prevents the race. Cc: stable@vger.kernel.org Signed-off-by: Jann Horn Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 6f6ff9b20981..7f439166baed 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1978,7 +1978,7 @@ static int smack_file_send_sigiotask(struct task_struct *tsk, { struct smack_known **blob; struct smack_known *skp; - struct smack_known *tkp = smk_of_task(smack_cred(tsk->cred)); + struct smack_known *tkp = smk_of_task_struct_obj(tsk); const struct cred *tcred; struct file *file; int rc;