Skip to content

Commit b5628b8

Browse files
committed
Merge tag 'selinux-pr-20250527' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux
Pull selinux updates from Paul Moore: - Reduce the SELinux impact on path walks. Add a small directory access cache to the per-task SELinux state. This cache allows SELinux to cache the most recently used directory access decisions in order to avoid repeatedly querying the AVC on path walks where the majority of the directories have similar security contexts/labels. My performance measurements are crude, but prior to this patch the time spent in SELinux code on a 'make allmodconfig' run was 103% that of __d_lookup_rcu(), and with this patch the time spent in SELinux code dropped to 63% of __d_lookup_rcu(), a ~40% improvement. Additional improvments can be expected in the future, but those will require additional SELinux policy/toolchain support. - Add support for wildcards in genfscon policy statements. This patch allows for wildcards in the genfscon patch matching logic as opposed to the prefix matching that was used prior to this change. Adding wilcard support allows for more expressive and efficient path matching in the policy which is especially helpful for sysfs, and has resulted in a ~15% boot time reduction in Android. SELinux policies can opt into wilcard matching by using the "genfs_seclabel_wildcard" policy capability. - Unify the error/OOM handling of the SELinux network caches. A failure to allocate memory for the SELinux network caches isn't fatal as the object label can still be safely returned to the caller, it simply means that we cannot add the new data to the cache, at least temporarily. This patch corrects this behavior for the InfiniBand cache and does some minor cleanup. - Minor improvements around constification, 'likely' annotations, and removal of bogus comments. * tag 'selinux-pr-20250527' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux: selinux: fix the kdoc header for task_avdcache_update selinux: remove a duplicated include selinux: reduce path walk overhead selinux: support wildcard match in genfscon selinux: drop copy-paste comment selinux: unify OOM handling in network hashtables selinux: add likely hints for fast paths selinux: contify network namespace pointer selinux: constify network address pointer
2 parents 1bc8c83 + 05f1a93 commit b5628b8

File tree

11 files changed

+232
-85
lines changed

11 files changed

+232
-85
lines changed

security/selinux/hooks.c

Lines changed: 171 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -213,8 +213,10 @@ static void cred_init_security(void)
213213
{
214214
struct task_security_struct *tsec;
215215

216+
/* NOTE: the lsm framework zeros out the buffer on allocation */
217+
216218
tsec = selinux_cred(unrcu_pointer(current->real_cred));
217-
tsec->osid = tsec->sid = SECINITSID_KERNEL;
219+
tsec->osid = tsec->sid = tsec->avdcache.sid = SECINITSID_KERNEL;
218220
}
219221

220222
/*
@@ -278,27 +280,21 @@ static int __inode_security_revalidate(struct inode *inode,
278280
struct dentry *dentry,
279281
bool may_sleep)
280282
{
281-
struct inode_security_struct *isec = selinux_inode(inode);
283+
if (!selinux_initialized())
284+
return 0;
282285

283-
might_sleep_if(may_sleep);
286+
if (may_sleep)
287+
might_sleep();
288+
else
289+
return -ECHILD;
284290

285291
/*
286-
* The check of isec->initialized below is racy but
287-
* inode_doinit_with_dentry() will recheck with
288-
* isec->lock held.
292+
* Check to ensure that an inode's SELinux state is valid and try
293+
* reloading the inode security label if necessary. This will fail if
294+
* @dentry is NULL and no dentry for this inode can be found; in that
295+
* case, continue using the old label.
289296
*/
290-
if (selinux_initialized() &&
291-
data_race(isec->initialized != LABEL_INITIALIZED)) {
292-
if (!may_sleep)
293-
return -ECHILD;
294-
295-
/*
296-
* Try reloading the inode security label. This will fail if
297-
* @opt_dentry is NULL and no dentry for this inode can be
298-
* found; in that case, continue using the old label.
299-
*/
300-
inode_doinit_with_dentry(inode, dentry);
301-
}
297+
inode_doinit_with_dentry(inode, dentry);
302298
return 0;
303299
}
304300

@@ -307,41 +303,53 @@ static struct inode_security_struct *inode_security_novalidate(struct inode *ino
307303
return selinux_inode(inode);
308304
}
309305

310-
static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
306+
static inline struct inode_security_struct *inode_security_rcu(struct inode *inode,
307+
bool rcu)
311308
{
312-
int error;
309+
int rc;
310+
struct inode_security_struct *isec = selinux_inode(inode);
313311

314-
error = __inode_security_revalidate(inode, NULL, !rcu);
315-
if (error)
316-
return ERR_PTR(error);
317-
return selinux_inode(inode);
312+
/* check below is racy, but revalidate will recheck with lock held */
313+
if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
314+
return isec;
315+
rc = __inode_security_revalidate(inode, NULL, !rcu);
316+
if (rc)
317+
return ERR_PTR(rc);
318+
return isec;
318319
}
319320

320321
/*
321322
* Get the security label of an inode.
322323
*/
323-
static struct inode_security_struct *inode_security(struct inode *inode)
324+
static inline struct inode_security_struct *inode_security(struct inode *inode)
324325
{
326+
struct inode_security_struct *isec = selinux_inode(inode);
327+
328+
/* check below is racy, but revalidate will recheck with lock held */
329+
if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
330+
return isec;
325331
__inode_security_revalidate(inode, NULL, true);
326-
return selinux_inode(inode);
332+
return isec;
327333
}
328334

329-
static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
335+
static inline struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
330336
{
331-
struct inode *inode = d_backing_inode(dentry);
332-
333-
return selinux_inode(inode);
337+
return selinux_inode(d_backing_inode(dentry));
334338
}
335339

336340
/*
337341
* Get the security label of a dentry's backing inode.
338342
*/
339-
static struct inode_security_struct *backing_inode_security(struct dentry *dentry)
343+
static inline struct inode_security_struct *backing_inode_security(struct dentry *dentry)
340344
{
341345
struct inode *inode = d_backing_inode(dentry);
346+
struct inode_security_struct *isec = selinux_inode(inode);
342347

348+
/* check below is racy, but revalidate will recheck with lock held */
349+
if (data_race(likely(isec->initialized == LABEL_INITIALIZED)))
350+
return isec;
343351
__inode_security_revalidate(inode, dentry, true);
344-
return selinux_inode(inode);
352+
return isec;
345353
}
346354

347355
static void inode_free_security(struct inode *inode)
@@ -1683,12 +1691,15 @@ static inline int dentry_has_perm(const struct cred *cred,
16831691
struct dentry *dentry,
16841692
u32 av)
16851693
{
1686-
struct inode *inode = d_backing_inode(dentry);
16871694
struct common_audit_data ad;
1695+
struct inode *inode = d_backing_inode(dentry);
1696+
struct inode_security_struct *isec = selinux_inode(inode);
16881697

16891698
ad.type = LSM_AUDIT_DATA_DENTRY;
16901699
ad.u.dentry = dentry;
1691-
__inode_security_revalidate(inode, dentry, true);
1700+
/* check below is racy, but revalidate will recheck with lock held */
1701+
if (data_race(unlikely(isec->initialized != LABEL_INITIALIZED)))
1702+
__inode_security_revalidate(inode, dentry, true);
16921703
return inode_has_perm(cred, inode, av, &ad);
16931704
}
16941705

@@ -1699,12 +1710,15 @@ static inline int path_has_perm(const struct cred *cred,
16991710
const struct path *path,
17001711
u32 av)
17011712
{
1702-
struct inode *inode = d_backing_inode(path->dentry);
17031713
struct common_audit_data ad;
1714+
struct inode *inode = d_backing_inode(path->dentry);
1715+
struct inode_security_struct *isec = selinux_inode(inode);
17041716

17051717
ad.type = LSM_AUDIT_DATA_PATH;
17061718
ad.u.path = *path;
1707-
__inode_security_revalidate(inode, path->dentry, true);
1719+
/* check below is racy, but revalidate will recheck with lock held */
1720+
if (data_race(unlikely(isec->initialized != LABEL_INITIALIZED)))
1721+
__inode_security_revalidate(inode, path->dentry, true);
17081722
return inode_has_perm(cred, inode, av, &ad);
17091723
}
17101724

@@ -3088,44 +3102,147 @@ static noinline int audit_inode_permission(struct inode *inode,
30883102
audited, denied, result, &ad);
30893103
}
30903104

3091-
static int selinux_inode_permission(struct inode *inode, int mask)
3105+
/**
3106+
* task_avdcache_reset - Reset the task's AVD cache
3107+
* @tsec: the task's security state
3108+
*
3109+
* Clear the task's AVD cache in @tsec and reset it to the current policy's
3110+
* and task's info.
3111+
*/
3112+
static inline void task_avdcache_reset(struct task_security_struct *tsec)
3113+
{
3114+
memset(&tsec->avdcache.dir, 0, sizeof(tsec->avdcache.dir));
3115+
tsec->avdcache.sid = tsec->sid;
3116+
tsec->avdcache.seqno = avc_policy_seqno();
3117+
tsec->avdcache.dir_spot = TSEC_AVDC_DIR_SIZE - 1;
3118+
}
3119+
3120+
/**
3121+
* task_avdcache_search - Search the task's AVD cache
3122+
* @tsec: the task's security state
3123+
* @isec: the inode to search for in the cache
3124+
* @avdc: matching avd cache entry returned to the caller
3125+
*
3126+
* Search @tsec for a AVD cache entry that matches @isec and return it to the
3127+
* caller via @avdc. Returns 0 if a match is found, negative values otherwise.
3128+
*/
3129+
static inline int task_avdcache_search(struct task_security_struct *tsec,
3130+
struct inode_security_struct *isec,
3131+
struct avdc_entry **avdc)
3132+
{
3133+
int orig, iter;
3134+
3135+
/* focused on path walk optimization, only cache directories */
3136+
if (isec->sclass != SECCLASS_DIR)
3137+
return -ENOENT;
3138+
3139+
if (unlikely(tsec->sid != tsec->avdcache.sid ||
3140+
tsec->avdcache.seqno != avc_policy_seqno())) {
3141+
task_avdcache_reset(tsec);
3142+
return -ENOENT;
3143+
}
3144+
3145+
orig = iter = tsec->avdcache.dir_spot;
3146+
do {
3147+
if (tsec->avdcache.dir[iter].isid == isec->sid) {
3148+
/* cache hit */
3149+
tsec->avdcache.dir_spot = iter;
3150+
*avdc = &tsec->avdcache.dir[iter];
3151+
return 0;
3152+
}
3153+
iter = (iter - 1) & (TSEC_AVDC_DIR_SIZE - 1);
3154+
} while (iter != orig);
3155+
3156+
return -ENOENT;
3157+
}
3158+
3159+
/**
3160+
* task_avdcache_update - Update the task's AVD cache
3161+
* @tsec: the task's security state
3162+
* @isec: the inode associated with the cache entry
3163+
* @avd: the AVD to cache
3164+
* @audited: the permission audit bitmask to cache
3165+
*
3166+
* Update the AVD cache in @tsec with the @avdc and @audited info associated
3167+
* with @isec.
3168+
*/
3169+
static inline void task_avdcache_update(struct task_security_struct *tsec,
3170+
struct inode_security_struct *isec,
3171+
struct av_decision *avd,
3172+
u32 audited)
30923173
{
3174+
int spot;
3175+
3176+
/* focused on path walk optimization, only cache directories */
3177+
if (isec->sclass != SECCLASS_DIR)
3178+
return;
3179+
3180+
/* update cache */
3181+
spot = (tsec->avdcache.dir_spot + 1) & (TSEC_AVDC_DIR_SIZE - 1);
3182+
tsec->avdcache.dir_spot = spot;
3183+
tsec->avdcache.dir[spot].isid = isec->sid;
3184+
tsec->avdcache.dir[spot].audited = audited;
3185+
tsec->avdcache.dir[spot].allowed = avd->allowed;
3186+
tsec->avdcache.dir[spot].permissive = avd->flags & AVD_FLAGS_PERMISSIVE;
3187+
}
3188+
3189+
/**
3190+
* selinux_inode_permission - Check if the current task can access an inode
3191+
* @inode: the inode that is being accessed
3192+
* @requested: the accesses being requested
3193+
*
3194+
* Check if the current task is allowed to access @inode according to
3195+
* @requested. Returns 0 if allowed, negative values otherwise.
3196+
*/
3197+
static int selinux_inode_permission(struct inode *inode, int requested)
3198+
{
3199+
int mask;
30933200
u32 perms;
3094-
bool from_access;
3095-
bool no_block = mask & MAY_NOT_BLOCK;
3201+
struct task_security_struct *tsec;
30963202
struct inode_security_struct *isec;
3097-
u32 sid = current_sid();
3098-
struct av_decision avd;
3203+
struct avdc_entry *avdc;
30993204
int rc, rc2;
31003205
u32 audited, denied;
31013206

3102-
from_access = mask & MAY_ACCESS;
3103-
mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
3207+
mask = requested & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
31043208

31053209
/* No permission to check. Existence test. */
31063210
if (!mask)
31073211
return 0;
31083212

3109-
if (unlikely(IS_PRIVATE(inode)))
3110-
return 0;
3111-
3112-
perms = file_mask_to_av(inode->i_mode, mask);
3113-
3114-
isec = inode_security_rcu(inode, no_block);
3213+
isec = inode_security_rcu(inode, requested & MAY_NOT_BLOCK);
31153214
if (IS_ERR(isec))
31163215
return PTR_ERR(isec);
3216+
tsec = selinux_cred(current_cred());
3217+
perms = file_mask_to_av(inode->i_mode, mask);
3218+
3219+
rc = task_avdcache_search(tsec, isec, &avdc);
3220+
if (likely(!rc)) {
3221+
/* Cache hit. */
3222+
audited = perms & avdc->audited;
3223+
denied = perms & ~avdc->allowed;
3224+
if (unlikely(denied && enforcing_enabled() &&
3225+
!avdc->permissive))
3226+
rc = -EACCES;
3227+
} else {
3228+
struct av_decision avd;
3229+
3230+
/* Cache miss. */
3231+
rc = avc_has_perm_noaudit(tsec->sid, isec->sid, isec->sclass,
3232+
perms, 0, &avd);
3233+
audited = avc_audit_required(perms, &avd, rc,
3234+
(requested & MAY_ACCESS) ? FILE__AUDIT_ACCESS : 0,
3235+
&denied);
3236+
task_avdcache_update(tsec, isec, &avd, audited);
3237+
}
31173238

3118-
rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0,
3119-
&avd);
3120-
audited = avc_audit_required(perms, &avd, rc,
3121-
from_access ? FILE__AUDIT_ACCESS : 0,
3122-
&denied);
31233239
if (likely(!audited))
31243240
return rc;
31253241

31263242
rc2 = audit_inode_permission(inode, perms, audited, denied, rc);
31273243
if (rc2)
31283244
return rc2;
3245+
31293246
return rc;
31303247
}
31313248

security/selinux/ibpkey.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
130130
{
131131
int ret;
132132
struct sel_ib_pkey *pkey;
133-
struct sel_ib_pkey *new = NULL;
133+
struct sel_ib_pkey *new;
134134
unsigned long flags;
135135

136136
spin_lock_irqsave(&sel_ib_pkey_lock, flags);
@@ -146,12 +146,11 @@ static int sel_ib_pkey_sid_slow(u64 subnet_prefix, u16 pkey_num, u32 *sid)
146146
if (ret)
147147
goto out;
148148

149-
/* If this memory allocation fails still return 0. The SID
150-
* is valid, it just won't be added to the cache.
151-
*/
152-
new = kzalloc(sizeof(*new), GFP_ATOMIC);
149+
new = kmalloc(sizeof(*new), GFP_ATOMIC);
153150
if (!new) {
154-
ret = -ENOMEM;
151+
/* If this memory allocation fails still return 0. The SID
152+
* is valid, it just won't be added to the cache.
153+
*/
155154
goto out;
156155
}
157156

@@ -184,7 +183,7 @@ int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey_num, u32 *sid)
184183

185184
rcu_read_lock();
186185
pkey = sel_ib_pkey_find(subnet_prefix, pkey_num);
187-
if (pkey) {
186+
if (likely(pkey)) {
188187
*sid = pkey->psec.sid;
189188
rcu_read_unlock();
190189
return 0;

security/selinux/include/netnode.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121

2222
void sel_netnode_flush(void);
2323

24-
int sel_netnode_sid(void *addr, u16 family, u32 *sid);
24+
int sel_netnode_sid(const void *addr, u16 family, u32 *sid);
2525

2626
#endif

0 commit comments

Comments
 (0)