Import of kernel-5.14.0-611.20.1.el9_7

This commit is contained in:
almalinux-bot-kernel 2026-01-16 04:19:21 +00:00
parent e562c02649
commit 09329b4587
54 changed files with 953 additions and 76 deletions

View File

@ -12,7 +12,7 @@ RHEL_MINOR = 7
#
# Use this spot to avoid future merge conflicts.
# Do not trim this comment.
RHEL_RELEASE = 611.16.1
RHEL_RELEASE = 611.20.1
#
# ZSTREAM

View File

@ -1447,6 +1447,14 @@ static __u8 *mt_report_fixup(struct hid_device *hdev, __u8 *rdesc,
if (hdev->vendor == I2C_VENDOR_ID_GOODIX &&
(hdev->product == I2C_DEVICE_ID_GOODIX_01E8 ||
hdev->product == I2C_DEVICE_ID_GOODIX_01E9)) {
if (*size < 608) {
dev_info(
&hdev->dev,
"GT7868Q fixup: report descriptor is only %u bytes, skipping\n",
*size);
return rdesc;
}
if (rdesc[607] == 0x15) {
rdesc[607] = 0x25;
dev_info(

View File

@ -3526,8 +3526,64 @@ static int partition_tape(struct scsi_tape *STp, int size)
out:
return result;
}
/*
* Handles any extra state needed for ioctls which are not st-specific.
* Called with the scsi_tape lock held, released before return
*/
static long st_common_ioctl(struct scsi_tape *STp, struct st_modedef *STm,
struct file *file, unsigned int cmd_in,
unsigned long arg)
{
int i, retval = 0;
if (!STm->defined) {
retval = -ENXIO;
goto out;
}
switch (cmd_in) {
case SCSI_IOCTL_GET_IDLUN:
case SCSI_IOCTL_GET_BUS_NUMBER:
case SCSI_IOCTL_GET_PCI:
break;
case SG_IO:
case SCSI_IOCTL_SEND_COMMAND:
case CDROM_SEND_PACKET:
if (!capable(CAP_SYS_RAWIO)) {
retval = -EPERM;
goto out;
}
fallthrough;
default:
if ((i = flush_buffer(STp, 0)) < 0) {
retval = i;
goto out;
} else { /* flush_buffer succeeds */
if (STp->can_partitions) {
i = switch_partition(STp);
if (i < 0) {
retval = i;
goto out;
}
}
}
}
mutex_unlock(&STp->lock);
retval = scsi_ioctl(STp->device, file->f_mode & FMODE_WRITE,
cmd_in, (void __user *)arg);
if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) {
/* unload */
STp->rew_at_close = 0;
STp->ready = ST_NO_TAPE;
}
return retval;
out:
mutex_unlock(&STp->lock);
return retval;
}
/* The ioctl command */
static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
@ -3565,6 +3621,15 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
if (retval)
goto out;
switch (cmd_in) {
case MTIOCPOS:
case MTIOCGET:
case MTIOCTOP:
break;
default:
return st_common_ioctl(STp, STm, file, cmd_in, arg);
}
cmd_type = _IOC_TYPE(cmd_in);
cmd_nr = _IOC_NR(cmd_in);
@ -3876,29 +3941,7 @@ static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
}
mt_pos.mt_blkno = blk;
retval = put_user_mtpos(p, &mt_pos);
goto out;
}
mutex_unlock(&STp->lock);
switch (cmd_in) {
case SG_IO:
case SCSI_IOCTL_SEND_COMMAND:
case CDROM_SEND_PACKET:
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
break;
default:
break;
}
retval = scsi_ioctl(STp->device, file->f_mode & FMODE_WRITE, cmd_in, p);
if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) {
/* unload */
STp->rew_at_close = 0;
STp->ready = ST_NO_TAPE;
}
return retval;
out:
mutex_unlock(&STp->lock);
return retval;

View File

@ -994,6 +994,11 @@ void nfs_delegation_mark_returned(struct inode *inode,
}
nfs_mark_delegation_revoked(delegation);
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
spin_unlock(&delegation->lock);
if (nfs_detach_delegation(NFS_I(inode), delegation, NFS_SERVER(inode)))
nfs_put_delegation(delegation);
goto out_rcu_unlock;
out_clear_returning:
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);

View File

@ -600,6 +600,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
extern struct TCP_Server_Info *
cifs_find_tcp_session(struct smb3_fs_context *ctx);
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal);
void __cifs_put_smb_ses(struct cifs_ses *ses);
extern struct cifs_ses *

View File

@ -2017,39 +2017,31 @@ static int match_session(struct cifs_ses *ses,
/**
* cifs_setup_ipc - helper to setup the IPC tcon for the session
* @ses: smb session to issue the request on
* @ctx: the superblock configuration context to use for building the
* new tree connection for the IPC (interprocess communication RPC)
* @seal: if encryption is requested
*
* A new IPC connection is made and stored in the session
* tcon_ipc. The IPC tcon has the same lifetime as the session.
*/
static int
cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal)
{
int rc = 0, xid;
struct cifs_tcon *tcon;
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
bool seal = false;
struct TCP_Server_Info *server = ses->server;
/*
* If the mount request that resulted in the creation of the
* session requires encryption, force IPC to be encrypted too.
*/
if (ctx->seal) {
if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
seal = true;
else {
cifs_server_dbg(VFS,
"IPC: server doesn't support encryption\n");
return -EOPNOTSUPP;
}
if (seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) {
cifs_server_dbg(VFS, "IPC: server doesn't support encryption\n");
return ERR_PTR(-EOPNOTSUPP);
}
/* no need to setup directory caching on IPC share, so pass in false */
tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
if (tcon == NULL)
return -ENOMEM;
return ERR_PTR(-ENOMEM);
spin_lock(&server->srv_lock);
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
@ -2059,13 +2051,13 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
tcon->ses = ses;
tcon->ipc = true;
tcon->seal = seal;
rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
rc = server->ops->tree_connect(xid, ses, unc, tcon, ses->local_nls);
free_xid(xid);
if (rc) {
cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
cifs_server_dbg(VFS | ONCE, "failed to connect to IPC (rc=%d)\n", rc);
tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
goto out;
return ERR_PTR(rc);
}
cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid);
@ -2073,9 +2065,7 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
spin_lock(&tcon->tc_lock);
tcon->status = TID_GOOD;
spin_unlock(&tcon->tc_lock);
ses->tcon_ipc = tcon;
out:
return rc;
return tcon;
}
static struct cifs_ses *
@ -2349,6 +2339,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
{
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
struct cifs_tcon *ipc;
struct cifs_ses *ses;
unsigned int xid;
int retries = 0;
@ -2527,7 +2518,12 @@ retry_new_session:
list_add(&ses->smb_ses_list, &server->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);
cifs_setup_ipc(ses, ctx);
ipc = cifs_setup_ipc(ses, ctx->seal);
spin_lock(&cifs_tcp_ses_lock);
spin_lock(&ses->ses_lock);
ses->tcon_ipc = !IS_ERR(ipc) ? ipc : NULL;
spin_unlock(&ses->ses_lock);
spin_unlock(&cifs_tcp_ses_lock);
free_xid(xid);

View File

@ -1120,24 +1120,63 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
return match;
}
static bool is_ses_good(struct cifs_ses *ses)
static bool is_ses_good(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
struct TCP_Server_Info *server = ses->server;
struct cifs_tcon *tcon = ses->tcon_ipc;
struct cifs_tcon *ipc = NULL;
bool ret;
spin_lock(&cifs_tcp_ses_lock);
spin_lock(&ses->ses_lock);
spin_lock(&ses->chan_lock);
ret = !cifs_chan_needs_reconnect(ses, server) &&
ses->ses_status == SES_GOOD &&
!tcon->need_reconnect;
ses->ses_status == SES_GOOD;
spin_unlock(&ses->chan_lock);
if (!ret)
goto out;
if (likely(ses->tcon_ipc)) {
if (ses->tcon_ipc->need_reconnect) {
ret = false;
goto out;
}
} else {
spin_unlock(&ses->ses_lock);
spin_unlock(&cifs_tcp_ses_lock);
ipc = cifs_setup_ipc(ses, tcon->seal);
spin_lock(&cifs_tcp_ses_lock);
spin_lock(&ses->ses_lock);
if (!IS_ERR(ipc)) {
if (!ses->tcon_ipc) {
ses->tcon_ipc = ipc;
ipc = NULL;
}
} else {
ret = false;
ipc = NULL;
}
}
out:
spin_unlock(&ses->ses_lock);
spin_unlock(&cifs_tcp_ses_lock);
if (ipc && server->ops->tree_disconnect) {
unsigned int xid = get_xid();
(void)server->ops->tree_disconnect(xid, ipc);
_free_xid(xid);
}
tconInfoFree(ipc, netfs_trace_tcon_ref_free_ipc);
return ret;
}
/* Refresh dfs referral of @ses */
static void refresh_ses_referral(struct cifs_ses *ses)
static void refresh_ses_referral(struct cifs_tcon *tcon, struct cifs_ses *ses)
{
struct cache_entry *ce;
unsigned int xid;
@ -1153,7 +1192,7 @@ static void refresh_ses_referral(struct cifs_ses *ses)
}
ses = CIFS_DFS_ROOT_SES(ses);
if (!is_ses_good(ses)) {
if (!is_ses_good(tcon, ses)) {
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
__func__);
goto out;
@ -1241,7 +1280,7 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
up_read(&htable_rw_lock);
ses = CIFS_DFS_ROOT_SES(ses);
if (!is_ses_good(ses)) {
if (!is_ses_good(tcon, ses)) {
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
__func__);
goto out;
@ -1309,7 +1348,7 @@ void dfs_cache_refresh(struct work_struct *work)
tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
list_for_each_entry(ses, &tcon->dfs_ses_list, dlist)
refresh_ses_referral(ses);
refresh_ses_referral(tcon, ses);
refresh_tcon_referral(tcon, false);
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,

View File

@ -2462,11 +2462,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
}
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
do_rename_exit:
if (rc == 0) {
if (rc == 0)
d_move(from_dentry, to_dentry);
/* Force a new lookup */
d_drop(from_dentry);
}
cifs_put_tlink(tlink);
return rc;
}

View File

@ -57,9 +57,11 @@ struct landlock_ruleset_attr {
*
* - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI
* version.
* - %LANDLOCK_CREATE_RULESET_ERRATA: Get a bitmask of fixed issues.
*/
/* clang-format off */
#define LANDLOCK_CREATE_RULESET_VERSION (1U << 0)
#define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1)
/* clang-format on */
/**
@ -296,9 +298,12 @@ struct landlock_net_port_attr {
* - %LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: Restrict a sandboxed process from
* connecting to an abstract UNIX socket created by a process outside the
* related Landlock domain (e.g. a parent domain or a non-sandboxed process).
* - %LANDLOCK_SCOPE_SIGNAL: Restrict a sandboxed process from sending a signal
* to another process outside the domain.
*/
/* clang-format off */
#define LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET (1ULL << 0)
#define LANDLOCK_SCOPE_SIGNAL (1ULL << 1)
/* clang-format on*/
#endif /* _UAPI_LINUX_LANDLOCK_H */

View File

@ -1325,7 +1325,7 @@ int audit_compare_dname_path(const struct qstr *dname, const char *path, int par
/* handle trailing slashes */
pathlen -= parentlen;
while (p[pathlen - 1] == '/')
while (pathlen > 0 && p[pathlen - 1] == '/')
pathlen--;
if (pathlen != dlen)

View File

@ -2541,10 +2541,9 @@ int unpoison_memory(unsigned long pfn)
static DEFINE_RATELIMIT_STATE(unpoison_rs, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
if (!pfn_valid(pfn))
return -ENXIO;
p = pfn_to_page(pfn);
p = pfn_to_online_page(pfn);
if (!p)
return -EIO;
folio = page_folio(p);
mutex_lock(&mf_mutex);

View File

@ -169,13 +169,14 @@ next_chunk:
chunk->head_skb = chunk->skb;
/* skbs with "cover letter" */
if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len)
if (chunk->head_skb && chunk->skb->data_len == chunk->skb->len) {
if (WARN_ON(!skb_shinfo(chunk->skb)->frag_list)) {
__SCTP_INC_STATS(dev_net(chunk->skb->dev),
SCTP_MIB_IN_PKT_DISCARDS);
sctp_chunk_free(chunk);
goto next_chunk;
}
chunk->skb = skb_shinfo(chunk->skb)->frag_list;
if (WARN_ON(!chunk->skb)) {
__SCTP_INC_STATS(dev_net(chunk->skb->dev), SCTP_MIB_IN_PKT_DISCARDS);
sctp_chunk_free(chunk);
goto next_chunk;
}
}

View File

@ -93,6 +93,13 @@ ifndef RHJOBS
fi)
endif
# for official builds use RELEASE_LOCALVERSION persisted in Makefile.variables
ifneq ($(filter $(MAKECMDGOALS),dist-release dist-release-tag dist-release-git dist-rtg dist-get-tag),)
DISTLOCALVERSION:=$(RELEASE_LOCALVERSION)
ifeq (,$(findstring s,$(firstword -$(MAKEFLAGS))))
$(info DISTLOCALVERSION is "$(DISTLOCALVERSION)".)
endif
else
LOCVERFILE:=../localversion
# create an empty localversion file if you don't want a local buildid
ifneq ($(wildcard $(LOCVERFILE)),)
@ -110,6 +117,7 @@ else
endif
$(info DISTLOCALVERSION is "$(DISTLOCALVERSION)".)
endif
endif # MAKECMDGOALS
# options for process_configs.sh script
ifdef NO_CONFIGCHECKS

View File

@ -114,6 +114,10 @@ NO_CONFIGCHECKS ?=
# considered stable and may be changed or removed without warning.
RHSELFTESTDATA ?=
# Local version to be used for official (non-scratch) builds. Makefile
# dist-{release/tag/git} targets will ignore localversion and DISTLOCALVERSION
RELEASE_LOCALVERSION:=
# This variable is used by the redhat/self-tests. It should not be
# considered stable and my be changed or removed without warning.
RHDISTDATADIR ?=

View File

@ -1,3 +1,34 @@
* Sat Dec 20 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.20.1.el9_7]
- HID: multitouch: fix slab out-of-bounds access in mt_report_fixup() (CKI Backport Bot) [RHEL-124607] {CVE-2025-39806}
- sctp: avoid NULL dereference when chunk data buffer is missing (CKI Backport Bot) [RHEL-134001] {CVE-2025-40240}
- selftests/landlock: Add a new test for setuid() (Štěpán Horáček) [RHEL-132712]
- selftests/landlock: Split signal_scoping_threads tests (Štěpán Horáček) [RHEL-132712]
- landlock: Always allow signals between threads of the same process (Štěpán Horáček) [RHEL-132712]
- landlock: Prepare to add second errata (Štěpán Horáček) [RHEL-132712]
- landlock: Add the errata interface (Štěpán Horáček) [RHEL-132712]
- selftests/landlock: Test signal scoping for threads (Štěpán Horáček) [RHEL-132712]
- selftests/landlock: Test signal scoping (Štěpán Horáček) [RHEL-132712]
- landlock: Add signal scoping (Štěpán Horáček) [RHEL-132712]
Resolves: RHEL-124607, RHEL-132712, RHEL-134001
* Thu Dec 18 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.19.1.el9_7]
- scsi: st: Skip buffer flush for information ioctls (John Meneghini) [RHEL-133543]
- scsi: st: Separate st-unique ioctl handling from SCSI common ioctl handling (John Meneghini) [RHEL-133543]
- audit: fix out-of-bounds read in audit_compare_dname_path() (Richard Guy Briggs) [RHEL-119176] {CVE-2025-39840}
Resolves: RHEL-119176, RHEL-133543
* Sat Dec 13 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.18.1.el9_7]
- NFS: remove revoked delegation from server's delegation list (Benjamin Coddington) [RHEL-134237]
- redhat: use RELEASE_LOCALVERSION also for dist-get-tag (Jan Stancek)
- redhat: introduce RELEASE_LOCALVERSION variable (Jan Stancek)
Resolves: RHEL-134237
* Thu Dec 11 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.17.1.el9_7]
- smb: client: handle lack of IPC in dfs_cache_refresh() (Paulo Alcantara) [RHEL-126165]
- smb: client: get rid of d_drop() in cifs_do_rename() (Paulo Alcantara) [RHEL-124917]
- mm/memory-failure: fix VM_BUG_ON_PAGE(PagePoisoned(page)) when unpoison memory (CKI Backport Bot) [RHEL-119150] {CVE-2025-39883}
Resolves: RHEL-119150, RHEL-124917, RHEL-126165
* Sun Dec 07 2025 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [5.14.0-611.16.1.el9_7]
- CVE-2025-38499 kernel: clone_private_mnt(): make sure that caller has CAP_SYS_ADMIN in the right userns (Abhi Das) [RHEL-129261] {CVE-2025-38499}
- tls: wait for pending async decryptions if tls_strp_msg_hold fails (CKI Backport Bot) [RHEL-128860] {CVE-2025-40176}

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=c9s
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rawhide
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc4.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-0.rc5.6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -46,6 +46,7 @@ PROCESS_CONFIGS_CHECK_OPTS=-n -t -c
PROCESS_CONFIGS_OPTS=-n -w -c
REDHAT=../redhat
RELEASETAG=kernel-5.12.0-6.test
RELEASE_LOCALVERSION=
RHDISTGIT_BRANCH=rhel-9.7.0
RHDISTGIT_USER="shadowman"
RHEL_MAJOR=9

View File

@ -26,7 +26,7 @@ landlock_cred(const struct cred *cred)
return cred->security + landlock_blob_sizes.lbs_cred;
}
static inline const struct landlock_ruleset *landlock_get_current_domain(void)
static inline struct landlock_ruleset *landlock_get_current_domain(void)
{
return landlock_cred(current_cred())->domain;
}

View File

@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Landlock - Errata information
*
* Copyright © 2025 Microsoft Corporation
*/
#ifndef _SECURITY_LANDLOCK_ERRATA_H
#define _SECURITY_LANDLOCK_ERRATA_H
#include <linux/init.h>
struct landlock_erratum {
const int abi;
const u8 number;
};
/* clang-format off */
#define LANDLOCK_ERRATUM(NUMBER) \
{ \
.abi = LANDLOCK_ERRATA_ABI, \
.number = NUMBER, \
},
/* clang-format on */
/*
* Some fixes may require user space to check if they are applied on the running
* kernel before using a specific feature. For instance, this applies when a
* restriction was previously too restrictive and is now getting relaxed (for
* compatibility or semantic reasons). However, non-visible changes for
* legitimate use (e.g. security fixes) do not require an erratum.
*/
static const struct landlock_erratum landlock_errata_init[] __initconst = {
/*
* Only Sparse may not implement __has_include. If a compiler does not
* implement __has_include, a warning will be printed at boot time (see
* setup.c).
*/
#ifdef __has_include
#define LANDLOCK_ERRATA_ABI 1
#if __has_include("errata/abi-1.h")
#include "errata/abi-1.h"
#endif
#undef LANDLOCK_ERRATA_ABI
#define LANDLOCK_ERRATA_ABI 2
#if __has_include("errata/abi-2.h")
#include "errata/abi-2.h"
#endif
#undef LANDLOCK_ERRATA_ABI
#define LANDLOCK_ERRATA_ABI 3
#if __has_include("errata/abi-3.h")
#include "errata/abi-3.h"
#endif
#undef LANDLOCK_ERRATA_ABI
#define LANDLOCK_ERRATA_ABI 4
#if __has_include("errata/abi-4.h")
#include "errata/abi-4.h"
#endif
#undef LANDLOCK_ERRATA_ABI
#define LANDLOCK_ERRATA_ABI 5
#if __has_include("errata/abi-5.h")
#include "errata/abi-5.h"
#endif
#undef LANDLOCK_ERRATA_ABI
#define LANDLOCK_ERRATA_ABI 6
#if __has_include("errata/abi-6.h")
#include "errata/abi-6.h"
#endif
#undef LANDLOCK_ERRATA_ABI
/*
* For each new erratum, we need to include all the ABI files up to the impacted
* ABI to make all potential future intermediate errata easy to backport.
*
* If such change involves more than one ABI addition, then it must be in a
* dedicated commit with the same Fixes tag as used for the actual fix.
*
* Each commit creating a new security/landlock/errata/abi-*.h file must have a
* Depends-on tag to reference the commit that previously added the line to
* include this new file, except if the original Fixes tag is enough.
*
* Each erratum must be documented in its related ABI file, and a dedicated
* commit must update Documentation/userspace-api/landlock.rst to include this
* erratum. This commit will not be backported.
*/
#endif
{}
};
#endif /* _SECURITY_LANDLOCK_ERRATA_H */

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/**
* DOC: erratum_2
*
* Erratum 2: Scoped signal handling
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This fix addresses an issue where signal scoping was overly restrictive,
* preventing sandboxed threads from signaling other threads within the same
* process if they belonged to different domains. Because threads are not
* security boundaries, user space might assume that any thread within the same
* process can send signals between themselves (see :manpage:`nptl(7)` and
* :manpage:`libpsx(3)`). Consistent with :manpage:`ptrace(2)` behavior, direct
* interaction between threads of the same process should always be allowed.
* This change ensures that any thread is allowed to send signals to any other
* thread within the same process, regardless of their domain.
*/
LANDLOCK_ERRATUM(2)

View File

@ -27,7 +27,9 @@
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/path.h>
#include <linux/pid.h>
#include <linux/rcupdate.h>
#include <linux/sched/signal.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/types.h>
@ -1636,6 +1638,54 @@ static int hook_file_ioctl_compat(struct file *file, unsigned int cmd,
return -EACCES;
}
/*
* Always allow sending signals between threads of the same process. This
* ensures consistency with hook_task_kill().
*/
static bool control_current_fowner(struct fown_struct *const fown)
{
struct task_struct *p;
/*
* Lock already held by __f_setown(), see commit 26f204380a3c ("fs: Fix
* file_set_fowner LSM hook inconsistencies").
*/
lockdep_assert_held(&fown->lock);
/*
* Some callers (e.g. fcntl_dirnotify) may not be in an RCU read-side
* critical section.
*/
guard(rcu)();
p = pid_task(fown->pid, fown->pid_type);
if (!p)
return true;
return !same_thread_group(p, current);
}
static void hook_file_set_fowner(struct file *file)
{
struct landlock_ruleset *prev_dom;
struct landlock_ruleset *new_dom = NULL;
if (control_current_fowner(&file->f_owner)) {
new_dom = landlock_get_current_domain();
landlock_get_ruleset(new_dom);
}
prev_dom = landlock_file(file)->fown_domain;
landlock_file(file)->fown_domain = new_dom;
/* May be called in an RCU read-side critical section. */
landlock_put_ruleset_deferred(prev_dom);
}
static void hook_file_free_security(struct file *file)
{
landlock_put_ruleset_deferred(landlock_file(file)->fown_domain);
}
static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(inode_free_security, hook_inode_free_security),
@ -1660,6 +1710,8 @@ static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_truncate, hook_file_truncate),
LSM_HOOK_INIT(file_ioctl, hook_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, hook_file_ioctl_compat),
LSM_HOOK_INIT(file_set_fowner, hook_file_set_fowner),
LSM_HOOK_INIT(file_free_security, hook_file_free_security),
};
__init void landlock_add_fs_hooks(void)

View File

@ -52,6 +52,13 @@ struct landlock_file_security {
* needed to authorize later operations on the open file.
*/
access_mask_t allowed_access;
/**
* @fown_domain: Domain of the task that set the PID that may receive a
* signal e.g., SIGURG when writing MSG_OOB to the related socket.
* This pointer is protected by the related file->f_owner->lock, as for
* fown_struct's members: pid, uid, and euid.
*/
struct landlock_ruleset *fown_domain;
};
/**

View File

@ -26,7 +26,7 @@
#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
#define LANDLOCK_LAST_SCOPE LANDLOCK_SCOPE_SIGNAL
#define LANDLOCK_MASK_SCOPE ((LANDLOCK_LAST_SCOPE << 1) - 1)
#define LANDLOCK_NUM_SCOPE __const_hweight64(LANDLOCK_MASK_SCOPE)
/* clang-format on */

View File

@ -6,12 +6,14 @@
* Copyright © 2018-2020 ANSSI
*/
#include <linux/bits.h>
#include <linux/init.h>
#include <linux/lsm_hooks.h>
#include <uapi/linux/lsm.h>
#include "common.h"
#include "cred.h"
#include "errata.h"
#include "fs.h"
#include "net.h"
#include "setup.h"
@ -31,8 +33,36 @@ const struct lsm_id landlock_lsmid = {
.id = LSM_ID_LANDLOCK,
};
int landlock_errata __ro_after_init;
static void __init compute_errata(void)
{
size_t i;
#ifndef __has_include
/*
* This is a safeguard to make sure the compiler implements
* __has_include (see errata.h).
*/
WARN_ON_ONCE(1);
return;
#endif
for (i = 0; landlock_errata_init[i].number; i++) {
const int prev_errata = landlock_errata;
if (WARN_ON_ONCE(landlock_errata_init[i].abi >
landlock_abi_version))
continue;
landlock_errata |= BIT(landlock_errata_init[i].number - 1);
WARN_ON_ONCE(prev_errata == landlock_errata);
}
}
static int __init landlock_init(void)
{
compute_errata();
landlock_add_cred_hooks();
landlock_add_task_hooks();
landlock_add_fs_hooks();

View File

@ -11,7 +11,10 @@
#include <linux/lsm_hooks.h>
extern const int landlock_abi_version;
extern bool landlock_initialized;
extern int landlock_errata;
extern struct lsm_blob_sizes landlock_blob_sizes;
extern const struct lsm_id landlock_lsmid;

View File

@ -159,7 +159,9 @@ static const struct file_operations ruleset_fops = {
* the new ruleset.
* @size: Size of the pointed &struct landlock_ruleset_attr (needed for
* backward and forward compatibility).
* @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION.
* @flags: Supported value:
* - %LANDLOCK_CREATE_RULESET_VERSION
* - %LANDLOCK_CREATE_RULESET_ERRATA
*
* This system call enables to create a new Landlock ruleset, and returns the
* related file descriptor on success.
@ -168,6 +170,10 @@ static const struct file_operations ruleset_fops = {
* 0, then the returned value is the highest supported Landlock ABI version
* (starting at 1).
*
* If @flags is %LANDLOCK_CREATE_RULESET_ERRATA and @attr is NULL and @size is
* 0, then the returned value is a bitmask of fixed issues for the current
* Landlock ABI version.
*
* Possible returned errors are:
*
* - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
@ -191,9 +197,15 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
return -EOPNOTSUPP;
if (flags) {
if ((flags == LANDLOCK_CREATE_RULESET_VERSION) && !attr &&
!size)
return LANDLOCK_ABI_VERSION;
if (attr || size)
return -EINVAL;
if (flags == LANDLOCK_CREATE_RULESET_VERSION)
return landlock_abi_version;
if (flags == LANDLOCK_CREATE_RULESET_ERRATA)
return landlock_errata;
return -EINVAL;
}
@ -234,6 +246,8 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
return ruleset_fd;
}
const int landlock_abi_version = LANDLOCK_ABI_VERSION;
/*
* Returns an owned ruleset from a FD. It is thus needed to call
* landlock_put_ruleset() on the return value.

View File

@ -13,11 +13,13 @@
#include <linux/lsm_hooks.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <net/af_unix.h>
#include <net/sock.h>
#include "common.h"
#include "cred.h"
#include "fs.h"
#include "ruleset.h"
#include "setup.h"
#include "task.h"
@ -242,12 +244,78 @@ static int hook_unix_may_send(struct socket *const sock,
return 0;
}
static int hook_task_kill(struct task_struct *const p,
struct kernel_siginfo *const info, const int sig,
const struct cred *const cred)
{
bool is_scoped;
const struct landlock_ruleset *dom;
if (cred) {
/* Dealing with USB IO. */
dom = landlock_cred(cred)->domain;
} else {
/*
* Always allow sending signals between threads of the same process.
* This is required for process credential changes by the Native POSIX
* Threads Library and implemented by the set*id(2) wrappers and
* libcap(3) with tgkill(2). See nptl(7) and libpsx(3).
*
* This exception is similar to the __ptrace_may_access() one.
*/
if (same_thread_group(p, current))
return 0;
dom = landlock_get_current_domain();
}
/* Quick return for non-landlocked tasks. */
if (!dom)
return 0;
rcu_read_lock();
is_scoped = domain_is_scoped(dom, landlock_get_task_domain(p),
LANDLOCK_SCOPE_SIGNAL);
rcu_read_unlock();
if (is_scoped)
return -EPERM;
return 0;
}
static int hook_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int signum)
{
const struct landlock_ruleset *dom;
bool is_scoped = false;
/* Lock already held by send_sigio() and send_sigurg(). */
lockdep_assert_held(&fown->lock);
dom = landlock_file(container_of(fown, struct file, f_owner))->fown_domain;
/* Quick return for unowned socket. */
if (!dom)
return 0;
rcu_read_lock();
is_scoped = domain_is_scoped(dom, landlock_get_task_domain(tsk),
LANDLOCK_SCOPE_SIGNAL);
rcu_read_unlock();
if (is_scoped)
return -EPERM;
return 0;
}
static struct security_hook_list landlock_hooks[] __ro_after_init = {
LSM_HOOK_INIT(ptrace_access_check, hook_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, hook_ptrace_traceme),
LSM_HOOK_INIT(unix_stream_connect, hook_unix_stream_connect),
LSM_HOOK_INIT(unix_may_send, hook_unix_may_send),
LSM_HOOK_INIT(task_kill, hook_task_kill),
LSM_HOOK_INIT(file_send_sigiotask, hook_file_send_sigiotask),
};
__init void landlock_add_task_hooks(void)

View File

@ -98,10 +98,54 @@ TEST(abi_version)
ASSERT_EQ(EINVAL, errno);
}
/*
* Old source trees might not have the set of Kselftest fixes related to kernel
* UAPI headers.
*/
#ifndef LANDLOCK_CREATE_RULESET_ERRATA
#define LANDLOCK_CREATE_RULESET_ERRATA (1U << 1)
#endif
TEST(errata)
{
const struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
};
int errata;
errata = landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_ERRATA);
/* The errata bitmask will not be backported to tests. */
ASSERT_LE(0, errata);
TH_LOG("errata: 0x%x", errata);
ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
LANDLOCK_CREATE_RULESET_ERRATA));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr),
LANDLOCK_CREATE_RULESET_ERRATA));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1,
landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr),
LANDLOCK_CREATE_RULESET_ERRATA));
ASSERT_EQ(EINVAL, errno);
ASSERT_EQ(-1, landlock_create_ruleset(
NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION |
LANDLOCK_CREATE_RULESET_ERRATA));
ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_ERRATA |
1 << 31));
ASSERT_EQ(EINVAL, errno);
}
/* Tests ordering of syscall argument checks. */
TEST(create_ruleset_checks_ordering)
{
const int last_flag = LANDLOCK_CREATE_RULESET_VERSION;
const int last_flag = LANDLOCK_CREATE_RULESET_ERRATA;
const int invalid_flag = last_flag << 1;
int ruleset_fd;
const struct landlock_ruleset_attr ruleset_attr = {

View File

@ -68,6 +68,7 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
CAP_MKNOD,
CAP_NET_ADMIN,
CAP_NET_BIND_SERVICE,
CAP_SETUID,
CAP_SYS_ADMIN,
CAP_SYS_CHROOT,
/* clang-format on */

View File

@ -0,0 +1,378 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Landlock tests - Signal Scoping
*
* Copyright © 2024 Tahera Fahimi <fahimitahera@gmail.com>
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <linux/landlock.h>
#include <pthread.h>
#include <signal.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "common.h"
#include "scoped_common.h"
/* This variable is used for handling several signals. */
static volatile sig_atomic_t is_signaled;
/* clang-format off */
FIXTURE(scoping_signals) {};
/* clang-format on */
FIXTURE_VARIANT(scoping_signals)
{
int sig;
};
/* clang-format off */
FIXTURE_VARIANT_ADD(scoping_signals, sigtrap) {
/* clang-format on */
.sig = SIGTRAP,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(scoping_signals, sigurg) {
/* clang-format on */
.sig = SIGURG,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(scoping_signals, sighup) {
/* clang-format on */
.sig = SIGHUP,
};
/* clang-format off */
FIXTURE_VARIANT_ADD(scoping_signals, sigtstp) {
/* clang-format on */
.sig = SIGTSTP,
};
FIXTURE_SETUP(scoping_signals)
{
drop_caps(_metadata);
is_signaled = 0;
}
FIXTURE_TEARDOWN(scoping_signals)
{
}
static void scope_signal_handler(int sig, siginfo_t *info, void *ucontext)
{
if (sig == SIGTRAP || sig == SIGURG || sig == SIGHUP || sig == SIGTSTP)
is_signaled = 1;
}
/*
* In this test, a child process sends a signal to parent before and
* after getting scoped.
*/
TEST_F(scoping_signals, send_sig_to_parent)
{
int pipe_parent[2];
int status;
pid_t child;
pid_t parent = getpid();
struct sigaction action = {
.sa_sigaction = scope_signal_handler,
.sa_flags = SA_SIGINFO,
};
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
ASSERT_LE(0, sigaction(variant->sig, &action, NULL));
/* The process should not have already been signaled. */
EXPECT_EQ(0, is_signaled);
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
char buf_child;
int err;
EXPECT_EQ(0, close(pipe_parent[1]));
/*
* The child process can send signal to parent when
* domain is not scoped.
*/
err = kill(parent, variant->sig);
ASSERT_EQ(0, err);
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
EXPECT_EQ(0, close(pipe_parent[0]));
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
/*
* The child process cannot send signal to the parent
* anymore.
*/
err = kill(parent, variant->sig);
ASSERT_EQ(-1, err);
ASSERT_EQ(EPERM, errno);
/*
* No matter of the domain, a process should be able to
* send a signal to itself.
*/
ASSERT_EQ(0, is_signaled);
ASSERT_EQ(0, raise(variant->sig));
ASSERT_EQ(1, is_signaled);
_exit(_metadata->exit_code);
return;
}
EXPECT_EQ(0, close(pipe_parent[0]));
/* Waits for a first signal to be received, without race condition. */
while (!is_signaled && !usleep(1))
;
ASSERT_EQ(1, is_signaled);
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
EXPECT_EQ(0, close(pipe_parent[1]));
is_signaled = 0;
ASSERT_EQ(child, waitpid(child, &status, 0));
if (WIFSIGNALED(status) || !WIFEXITED(status) ||
WEXITSTATUS(status) != EXIT_SUCCESS)
_metadata->exit_code = KSFT_FAIL;
EXPECT_EQ(0, is_signaled);
}
/* clang-format off */
FIXTURE(scoped_domains) {};
/* clang-format on */
#include "scoped_base_variants.h"
FIXTURE_SETUP(scoped_domains)
{
drop_caps(_metadata);
}
FIXTURE_TEARDOWN(scoped_domains)
{
}
/*
* This test ensures that a scoped process cannot send signal out of
* scoped domain.
*/
TEST_F(scoped_domains, check_access_signal)
{
pid_t child;
pid_t parent = getpid();
int status;
bool can_signal_child, can_signal_parent;
int pipe_parent[2], pipe_child[2];
char buf_parent;
int err;
can_signal_parent = !variant->domain_child;
can_signal_child = !variant->domain_parent;
if (variant->domain_both)
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
ASSERT_EQ(0, pipe2(pipe_child, O_CLOEXEC));
child = fork();
ASSERT_LE(0, child);
if (child == 0) {
char buf_child;
EXPECT_EQ(0, close(pipe_child[0]));
EXPECT_EQ(0, close(pipe_parent[1]));
if (variant->domain_child)
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
ASSERT_EQ(1, write(pipe_child[1], ".", 1));
EXPECT_EQ(0, close(pipe_child[1]));
/* Waits for the parent to send signals. */
ASSERT_EQ(1, read(pipe_parent[0], &buf_child, 1));
EXPECT_EQ(0, close(pipe_parent[0]));
err = kill(parent, 0);
if (can_signal_parent) {
ASSERT_EQ(0, err);
} else {
ASSERT_EQ(-1, err);
ASSERT_EQ(EPERM, errno);
}
/*
* No matter of the domain, a process should be able to
* send a signal to itself.
*/
ASSERT_EQ(0, raise(0));
_exit(_metadata->exit_code);
return;
}
EXPECT_EQ(0, close(pipe_parent[0]));
EXPECT_EQ(0, close(pipe_child[1]));
if (variant->domain_parent)
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
ASSERT_EQ(1, read(pipe_child[0], &buf_parent, 1));
EXPECT_EQ(0, close(pipe_child[0]));
err = kill(child, 0);
if (can_signal_child) {
ASSERT_EQ(0, err);
} else {
ASSERT_EQ(-1, err);
ASSERT_EQ(EPERM, errno);
}
ASSERT_EQ(0, raise(0));
ASSERT_EQ(1, write(pipe_parent[1], ".", 1));
EXPECT_EQ(0, close(pipe_parent[1]));
ASSERT_EQ(child, waitpid(child, &status, 0));
if (WIFSIGNALED(status) || !WIFEXITED(status) ||
WEXITSTATUS(status) != EXIT_SUCCESS)
_metadata->exit_code = KSFT_FAIL;
}
enum thread_return {
THREAD_INVALID = 0,
THREAD_SUCCESS = 1,
THREAD_ERROR = 2,
THREAD_TEST_FAILED = 3,
};
static void *thread_sync(void *arg)
{
const int pipe_read = *(int *)arg;
char buf;
if (read(pipe_read, &buf, 1) != 1)
return (void *)THREAD_ERROR;
return (void *)THREAD_SUCCESS;
}
TEST(signal_scoping_thread_before)
{
pthread_t no_sandbox_thread;
enum thread_return ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC));
ASSERT_EQ(0, pthread_create(&no_sandbox_thread, NULL, thread_sync,
&thread_pipe[0]));
/* Enforces restriction after creating the thread. */
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
EXPECT_EQ(0, pthread_kill(no_sandbox_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
EXPECT_EQ(0, close(thread_pipe[1]));
}
TEST(signal_scoping_thread_after)
{
pthread_t scoped_thread;
enum thread_return ret = THREAD_INVALID;
int thread_pipe[2];
drop_caps(_metadata);
ASSERT_EQ(0, pipe2(thread_pipe, O_CLOEXEC));
/* Enforces restriction before creating the thread. */
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
ASSERT_EQ(0, pthread_create(&scoped_thread, NULL, thread_sync,
&thread_pipe[0]));
EXPECT_EQ(0, pthread_kill(scoped_thread, 0));
EXPECT_EQ(1, write(thread_pipe[1], ".", 1));
EXPECT_EQ(0, pthread_join(scoped_thread, (void **)&ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
EXPECT_EQ(0, close(thread_pipe[0]));
EXPECT_EQ(0, close(thread_pipe[1]));
}
struct thread_setuid_args {
int pipe_read, new_uid;
};
void *thread_setuid(void *ptr)
{
const struct thread_setuid_args *arg = ptr;
char buf;
if (read(arg->pipe_read, &buf, 1) != 1)
return (void *)THREAD_ERROR;
/* libc's setuid() should update all thread's credentials. */
if (getuid() != arg->new_uid)
return (void *)THREAD_TEST_FAILED;
return (void *)THREAD_SUCCESS;
}
TEST(signal_scoping_thread_setuid)
{
struct thread_setuid_args arg;
pthread_t no_sandbox_thread;
enum thread_return ret = THREAD_INVALID;
int pipe_parent[2];
int prev_uid;
disable_caps(_metadata);
/* This test does not need to be run as root. */
prev_uid = getuid();
arg.new_uid = prev_uid + 1;
EXPECT_LT(0, arg.new_uid);
ASSERT_EQ(0, pipe2(pipe_parent, O_CLOEXEC));
arg.pipe_read = pipe_parent[0];
/* Capabilities must be set before creating a new thread. */
set_cap(_metadata, CAP_SETUID);
ASSERT_EQ(0, pthread_create(&no_sandbox_thread, NULL, thread_setuid,
&arg));
/* Enforces restriction after creating the thread. */
create_scoped_domain(_metadata, LANDLOCK_SCOPE_SIGNAL);
EXPECT_NE(arg.new_uid, getuid());
EXPECT_EQ(0, setuid(arg.new_uid));
EXPECT_EQ(arg.new_uid, getuid());
EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
EXPECT_EQ(0, pthread_join(no_sandbox_thread, (void **)&ret));
EXPECT_EQ(THREAD_SUCCESS, ret);
clear_cap(_metadata, CAP_SETUID);
EXPECT_EQ(0, close(pipe_parent[0]));
EXPECT_EQ(0, close(pipe_parent[1]));
}
TEST_HARNESS_MAIN

View File

@ -12,7 +12,7 @@
#include "common.h"
#define ACCESS_LAST LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET
#define ACCESS_LAST LANDLOCK_SCOPE_SIGNAL
TEST(ruleset_with_unknown_scope)
{