From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qk1-f171.google.com (mail-qk1-f171.google.com [209.85.222.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D50A53D9678 for ; Thu, 30 Jul 2026 23:45:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.171 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785455144; cv=none; b=E0rbdS3HkVNdi4NkcinXR964y3o7g47PWr7fNAF5XD1LqMg29q3+UfnsLT1PyKWoAgZQKTVDALLOYHu/KXTaUD/Vzsljc7yW7/EEX+kfl6tMjngB8Um1DYhh3QUBhCBQ5ISoRkRN+xHolDLoioPfeYsAOCSTyLhvK7N9/4Z1HlU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785455144; c=relaxed/simple; bh=1DTCO6Bphw0A6swBhP7c6bJyoFiSm1Y7s6/4c16HD9E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZfOnYlMOxSeZh3o5N7F22mgKfHsu8ygZUREvU6qmd3dJM5pCcjeAQGSe8DiL0gMLONWCQ9KkbwHgjb308f/iNmfYrfWE8kAl+km4y6gH3XoyDBKNZLBmCE2XLD9YvJ0rzdKJ5XcROvh8L0RfhJJcXza0WLY03z8HlL1auycnCYs= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IfjLWygk; arc=none smtp.client-ip=209.85.222.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IfjLWygk" Received: by mail-qk1-f171.google.com with SMTP id af79cd13be357-92e5d6f35c1so36884385a.0 for ; Thu, 30 Jul 2026 16:45:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785455141; x=1786059941; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jdbK3fdfrv5d4sVx1r+AaPHs8i/JcdSeTVatYSVLKYc=; b=IfjLWygk/NIv3NmVPxmrP0TVaSWqUaKbpCY7M9WzUtfFuEvvAXTDbmNJ/2QImMbswN p3ojJExcqU2ybISntSW7GlrGZNXFo0+L68/9R2OxhV+YLPaSrvO/oXASSbHcagwWGLSe CWeMpmdTq3+TQoXronRoU73X6YKjfONZRXQ6hjX5hyT3td56+psaOvhIDq+q0e9Tqskc BIM7UBcK5d/lVN7trRdHWPQPh0tLHd3dT8riSf74/wy7THJpzjHFZqYv+GiZ0dGvhjiK 3G/7qQen3YmJlZgK3SIfvN99ViIWa/7J3/20b8kqbrOPk661+TryN+3RBOH0oq9GAJWh pDHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785455141; x=1786059941; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jdbK3fdfrv5d4sVx1r+AaPHs8i/JcdSeTVatYSVLKYc=; b=I1D9Sh9VMfkRbJf66nHYrpHwQoRxqSiaNCyJhRBW8MIHWWqqWfkBAvIiE9Xhx2w8/C rCNhrtU0X6JcL8/Tu4Ooi7H8BwjJoFspxmjsnWNlBXiYK26dR0BQ6a6zqLTIdFjSZikr feQ4dtay/Fk65BD3JGN+5r9VHf6+HprvuReXKVbu9mEu9KxJTWmFpGi5mu4zIbhCdG33 6aSBJmZQczgwjUJwj4JEDECxEXk634OmpEko/fqgp7BEyWd9jfE9aXJde8DonCBxyhFz ru2gSoqPYpP24QMbEi8DJgMfpNEkVXWs8mJU6OL8ngsv+3KWoNQj5agSiDAsW14kzIhe 315Q== X-Forwarded-Encrypted: i=1; AHgh+RrprLzW8UMDICCMIzDqtmgqH9jcHla479+9Mqdvyf5Q5Z4or11psXmprEPtPPeNBrQqUDoe/kh9KFxVvz65@vger.kernel.org X-Gm-Message-State: AOJu0Yx1u1p0122iv11kCB08527X2qm2d4EIT90X8ceU6dYbC4efuObk 80LAWqroqQqS2GYsjcAMwpstaHC+OM/rOnvppUNziP++m80ogteaOReg X-Gm-Gg: AR+sD136xt1CVth7V9Tl1gKK9dr6utrHJtOpS4wvBk6sQegtZtc0bxvWjgSEw+Mon80 gPmmQdduwn74xYokv/GUOtGJTTp2fmRsyHPA3dsa051pceYIPzuLtv39UImM5XS14CRUMqsNr30 9fmwvk7vxciAnnSeNYwfEYHogFCMAxsoVW7zduqrAmMsviALkhcxBj1iqTji3rP7cXeqVn7z2yY EP/hnQcdlaI24uMOr75tnz3M2lO/EP0A00kg4Zob0/kl9K1aCRc11sXAN6FZ0WqstEWfOxgGy33 fef2+Od79RvygkHAqYVlevff0WjG7jKkM5iI4ISzjJAwhUn/nHUZdLE4X8AkjUPgLVH85fPCI+S lnFj7Jl6ZBpTVtAJIs+ogFxg655JEwdWAV9QHkjXHcPchQ5zSUPvLa0Hew+JGc/byL0aHlzdlp4 hcNtoyzxM9B2iHAO1yd4ITv+PofGSmD3ufdomTqG7L9dpIctuXFKGLxPe84NK7AlKj9v1TcoZ5q bMEOA3SW1yg16vYw2noCOReBT4Jcj7n8yBlXBAb3UUbnqBh01F426x6laZZsC0= X-Received: by 2002:a05:6214:5409:b0:8fe:9e2d:ce4f with SMTP id 6a1803df08f44-9083484849emr49532596d6.65.1785455140622; Thu, 30 Jul 2026 16:45:40 -0700 (PDT) Received: from battery.lan (pool-138-88-31-60.washdc.fios.verizon.net. [138.88.31.60]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-908323e8bc0sm28991836d6.26.2026.07.30.16.45.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 30 Jul 2026 16:45:40 -0700 (PDT) From: David Windsor To: Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Martin KaFai Lau , Eduard Zingerman , Song Liu , Yonghong Song , John Fastabend , KP Singh , Jiri Olsa , Kumar Kartikeya Dwivedi , Emil Tsalapatis , Matt Bobrowski , Paul Moore , James Morris , "Serge E . Hallyn" , Casey Schaufler , Stephen Smalley , Ondrej Mosnacek , Mimi Zohar , Roberto Sassu , Dmitry Kasatkin , Eric Snowberg , Alexander Viro , Christian Brauner , Jan Kara , Shuah Khan Cc: bpf@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fsdevel@vger.kernel.org, linux-integrity@vger.kernel.org, selinux@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-kernel@vger.kernel.org, David Windsor Subject: [PATCH v6 bpf-next 2/4] security: add security_lsmxattr_add() Date: Thu, 30 Jul 2026 19:45:31 -0400 Message-ID: <20260730234533.1912709-3-dwindsor@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260730234533.1912709-1-dwindsor@gmail.com> References: <20260730234533.1912709-1-dwindsor@gmail.com> Precedence: bulk X-Mailing-List: linux-fsdevel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Add security_lsmxattr_add(), which claims a slot in the inode_init_security xattr array on behalf of the calling LSM and fills it with a copy of the given name and value. Callers pass only the name components beyond their LSM's standard xattr suffix; security_lsmxattr_add() builds the full xattr name from the suffix associated with the given lsm_id. Suggested-by: Paul Moore Signed-off-by: David Windsor --- include/linux/bpf_lsm.h | 3 ++ include/linux/security.h | 10 +++++ security/bpf/hooks.c | 1 + security/security.c | 96 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index dda272d78f01..4bf350ef02f4 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -12,6 +12,9 @@ #include #include +/* max bpf xattrs per inode */ +#define BPF_LSM_INODE_INIT_XATTRS 4 + #ifdef CONFIG_BPF_LSM extern bool bpf_lsm_initialized __ro_after_init; diff --git a/include/linux/security.h b/include/linux/security.h index 0be590c40689..d35fde7aa11f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -406,6 +406,9 @@ void security_inode_free(struct inode *inode); int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, initxattrs initxattrs, void *fs_data); +int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, const void *value, + size_t value_len); int security_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode); @@ -900,6 +903,13 @@ static inline int security_inode_init_security(struct inode *inode, return 0; } +static inline int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, + const void *value, size_t value_len) +{ + return -EOPNOTSUPP; +} + static inline int security_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode) diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c index 7b98f5d1e2be..8f8c3de3035f 100644 --- a/security/bpf/hooks.c +++ b/security/bpf/hooks.c @@ -33,6 +33,7 @@ static int __init bpf_lsm_init(void) struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = { .lbs_inode = sizeof(struct bpf_storage_blob), + .lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS, }; DEFINE_LSM(bpf) = { diff --git a/security/security.c b/security/security.c index 2ad7f09c1a61..ae72102cd29b 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "LSM: " fmt #include +#include #include #include #include @@ -1376,6 +1377,101 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, } EXPORT_SYMBOL(security_inode_init_security); +static unsigned int lsm_xattrs_used(const struct lsm_xattrs *xattrs, + const char *prefix) +{ + size_t prefix_len = strlen(prefix); + unsigned int i, n = 0; + + for (i = 0; i < xattrs->xattr_count; i++) { + const char *name = xattrs->xattrs[i].name; + + if (name && !strncmp(name, prefix, prefix_len)) + n++; + } + return n; +} + +/** + * security_lsmxattr_add() - Add an xattr during inode_init_security + * @xattrs: xattr state shared by inode_init_security hooks + * @lsm_id: LSM_ID_* value identifying the calling LSM + * @name_extra: xattr name components beyond the calling LSM's standard + * xattr suffix, NULL if the standard suffix is the full name + * @value: xattr value + * @value_len: length of @value + * + * Claim an xattr slot in @xattrs on behalf of the LSM identified by + * @lsm_id and fill it with a copy of @value. The xattr name is built from + * the standard xattr suffix of the calling LSM, followed by @name_extra. + * Callers can invoke this function from non-sleepable context. + * + * Return: Returns 0 on success or if the filesystem does not accept xattrs + * at inode creation, -ENOSPC if the calling LSM's slot budget is + * exhausted, negative values on other errors. + */ +int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, const void *value, + size_t value_len) +{ + struct xattr *xattr; + void *xattr_value; + const char *suffix; + size_t suffix_len, extra_len, name_len; + + if (!xattrs || !value) + return -EINVAL; + + /* The filesystem did not provide an initxattrs callback. */ + if (!xattrs->xattrs) + return 0; + + switch (lsm_id) { + case LSM_ID_BPF: + if (!name_extra || !name_extra[0]) + return -EINVAL; + suffix = XATTR_BPF_LSM_SUFFIX; + if (lsm_xattrs_used(xattrs, XATTR_BPF_LSM_SUFFIX) >= + BPF_LSM_INODE_INIT_XATTRS) + return -ENOSPC; + break; + default: + return -EINVAL; + } + + suffix_len = strlen(suffix); + extra_len = name_extra ? strlen(name_extra) : 0; + name_len = suffix_len + extra_len; + if (name_len > XATTR_NAME_MAX) + return -EINVAL; + if (value_len == 0 || value_len > XATTR_SIZE_MAX) + return -EINVAL; + + /* Combine xattr value + name into one allocation. */ + xattr_value = kmalloc(value_len + name_len + 1, GFP_NOWAIT); + if (!xattr_value) + return -ENOMEM; + + memcpy(xattr_value, value, value_len); + memcpy(xattr_value + value_len, suffix, suffix_len); + if (extra_len) + memcpy(xattr_value + value_len + suffix_len, name_extra, + extra_len); + ((char *)xattr_value)[value_len + name_len] = '\0'; + + xattr = lsm_get_xattr_slot(xattrs); + if (!xattr) { + kfree(xattr_value); + return -ENOSPC; + } + + xattr->value = xattr_value; + xattr->name = (const char *)xattr_value + value_len; + xattr->value_len = value_len; + + return 0; +} + /** * security_inode_init_security_anon() - Initialize an anonymous inode * @inode: the inode -- 2.53.0