From nobody Tue Sep 29 04:09:42 2026 Received: from mail-pf1-f199.google.com (mail-pf1-f199.google.com [209.85.210.199]) (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 372592E62B7 for ; Thu, 13 Aug 2026 00:26:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580800; cv=none; b=DCYacuM1RN6JQCEOfI5xx5gMnYOLAZxFCviN3w/hcDVLF+8+sLnO2HGN40qIZ0QSfIxpTX4jbG+H+cJgGHAvOXzhUV4vTvgKYBUCrDQkUSO6gJcYFio89JR1IJaSomLQk6v0QPG5NgsCCjZbz2hjgGVf/WygXfdS61Vmbcrv/FI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580800; c=relaxed/simple; bh=PagIVbs3JZ91gND3Jax5qRoqj4Kakth/whntQuD8tlM=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qNVrtQfdveS8UVl1xV2OFqMOTBTP6DvGG/zLsps0tb2ff8KGtvRdBMv+qR9a/QSpXhtB5sh0ECHz03gnKDZkz84Koes0I2WGtLXZ+xFPWB8QRk7fqM2sprT0GzI4APtPruiIn0Abm0YIUX8YZt69c7k7xR+vsYnyjUNd9JBSiOY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=mqYxPUNh; arc=none smtp.client-ip=209.85.210.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mqYxPUNh" Received: by mail-pf1-f199.google.com with SMTP id d2e1a72fcca58-8488ac68185so238409b3a.2 for ; Wed, 12 Aug 2026 17:26:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786580799; x=1787185599; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=wOBAJjPbhXDDuiSCf0eRkBabtYoc0jLQAAGdFQiclE4=; b=mqYxPUNh1iP+2LTxI1wT0N73eR4sTUTzEf63mlFyddy0IAxEsvfgzCcv6dVklkOxYR 9jUZa5NrAVe4H0bfhPZRZMxEeLkdmY0uCAytnIEASgtn5u8/wEAIBXVA1C34Hrt8d1rk +24yNIL1hWh5vBnqy2wDdUcrlkh3NiT4Vej02g8ChhV+hvo8+FDbcKT4jR6V8m+6xD9O ZVdms+BVWfIBs9aZGrw/Y4jXHMQ0/TsdXuUdgC2RgIwvU+zmRfTPM8hdUe37h5pUileV 4psQbPQbxVcwMWToFrtLWbm/YvUMHC0NFmKu1p6/KZNXVDFyXPai4+3CWOr8vwaxxnA5 N20g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786580799; x=1787185599; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=wOBAJjPbhXDDuiSCf0eRkBabtYoc0jLQAAGdFQiclE4=; b=HCrSuJcLrTMeeac2EPTbKD6WOyEv0Ky/ed4HspeNgRNqmDc4ou1VguisXObDFlna2G 5QgRxsam6e+G37freAdm7KyOM1TaSZ74RCD6ghZdcCMGwz6rjwEL+hmX6UqlattyZz+Q VfYhs/wDl8bAHuHkt8V/o78wNb4h3HaU9P4RBpT3N1UTncyby8DLir6hvJoAgO7S1shc SlY7wCqRrzGL+UUq7DtdeoqnfoNlYxQhMCsooMzjeZ4awkgdrSvjoirp1GWtxDDEaqf8 /m9NyO5JfB/m87yuTbZqngSng+ai7W3ky67TGL+pD2Z7KshfCXAFuX4jh/aw96VXCV/v Z1kw== X-Forwarded-Encrypted: i=1; AHgh+Rqx5YK69O+Og+1CLiK0Fjn29WIhzoE2gjI3yZyvSodPdeq8Zyyo6rlub0DeSS9aDFp0msNzEvZ2RUlLn8o=@vger.kernel.org X-Gm-Message-State: AOJu0YytFcGFkGIi3ZvS9fI8AmvDz0HSag+TlexS4OXCOhFim4dVLvjQ NlHlM/R/S/x9CvGG53/ieYnjIKK8XpXkCdvxxR8eDqOHprhuZ3McX+45pAw6bISrQpoBpuIQuZk vgg== X-Received: from pfd4.prod.google.com ([2002:a05:6a00:a804:b0:84f:b437:398d]) (user=tweek job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:1151:b0:84f:a7bf:8fb9 with SMTP id d2e1a72fcca58-84fc749372emr1758140b3a.3.1786580798413; Wed, 12 Aug 2026 17:26:38 -0700 (PDT) Date: Thu, 13 Aug 2026 10:26:14 +1000 In-Reply-To: <20260813002618.3755631-1-tweek@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260813002618.3755631-1-tweek@google.com> X-Mailer: git-send-email 2.55.0.691.gc56d675ccc-goog Message-ID: <20260813002618.3755631-2-tweek@google.com> Subject: [PATCH bpf-next 1/5] fs/kernel_read_file,selinux: Add BPF_LOADER constant From: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" To: Paul Moore , Stephen Smalley , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Jeffrey Vander Stoep Cc: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" , Ondrej Mosnacek , Eric Suen , Blaise Boscaccy , Sid Nayyar , Neill Kapron , Eric Biggers , Greg Kroah-Hartman , KP Singh , bpf@vger.kernel.org, selinux@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add a new constant for kernel_read_file when loading a BPF loader. Add the matching SELinux policy for that constant. Signed-off-by: Thi=C3=A9baud Weksteen --- include/linux/kernel_read_file.h | 1 + security/selinux/hooks.c | 12 ++++++++++-- security/selinux/include/classmap.h | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_f= ile.h index d613a7b4dd35..fbcaf41c1b73 100644 --- a/include/linux/kernel_read_file.h +++ b/include/linux/kernel_read_file.h @@ -15,6 +15,7 @@ id(POLICY, security-policy) \ id(X509_CERTIFICATE, x509-certificate) \ id(MODULE_COMPRESSED, kernel-module-compressed) \ + id(BPF_LOADER, bpf-loader) \ id(MAX_ID, ) =20 #define __fid_enumify(ENUM, dummy) READING_ ## ENUM, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 18dd28b2bb13..f197cf476190 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4411,7 +4411,7 @@ static int selinux_kernel_read_file(struct file *file, { int rc =3D 0; =20 - BUILD_BUG_ON_MSG(READING_MAX_ID > 8, + BUILD_BUG_ON_MSG(READING_MAX_ID > 9, "New kernel_read_file_id introduced; update SELinux!"); =20 switch (id) { @@ -4437,6 +4437,10 @@ static int selinux_kernel_read_file(struct file *fil= e, rc =3D selinux_kernel_load_from_file(file, SYSTEM__X509_CERTIFICATE_LOAD); break; + case READING_BPF_LOADER: + rc =3D selinux_kernel_load_from_file(file, + SYSTEM__BPF_LOAD); + break; default: break; } @@ -4448,7 +4452,7 @@ static int selinux_kernel_load_data(enum kernel_load_= data_id id, bool contents) { int rc =3D 0; =20 - BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8, + BUILD_BUG_ON_MSG(LOADING_MAX_ID > 9, "New kernel_load_data_id introduced; update SELinux!"); =20 switch (id) { @@ -4474,6 +4478,10 @@ static int selinux_kernel_load_data(enum kernel_load= _data_id id, bool contents) rc =3D selinux_kernel_load_from_file(NULL, SYSTEM__X509_CERTIFICATE_LOAD); break; + case LOADING_BPF_LOADER: + rc =3D selinux_kernel_load_from_file(NULL, + SYSTEM__BPF_LOAD); + break; default: break; } diff --git a/security/selinux/include/classmap.h b/security/selinux/include= /classmap.h index 90cb61b16425..453522ca87df 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -65,7 +65,7 @@ const struct security_class_mapping secclass_map[] =3D { { "ipc_info", "syslog_read", "syslog_mod", "syslog_console", "module_request", "module_load", "firmware_load", "kexec_image_load", "kexec_initramfs_load", "policy_load", - "x509_certificate_load", NULL } }, + "x509_certificate_load", "bpf_load", NULL } }, { "capability", { COMMON_CAP_PERMS, NULL } }, { "filesystem", { "mount", "remount", "unmount", "getattr", "relabelfrom", --=20 2.55.0.691.gc56d675ccc-goog From nobody Tue Sep 29 04:09:42 2026 Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (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 A165113D539 for ; Thu, 13 Aug 2026 00:26:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580805; cv=none; b=E8KVD/2MQASWAesVIOmtHy2gxaMm3rXgN+oFANelHEjufhHEgwSMNU57eW8f/y70kcviVfRY1BZyYSfPmgkntu+V6BoRjJmVMxPQLZS8NcbIiAsgKrfwnLyjl3wf6M6kpGysyedMDKdXvhBkzoC7mki3px6xFClYD13YDauNPXA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580805; c=relaxed/simple; bh=9bxv8IEOD63iIg3rz+IGLTgoL3q6t205C7kkxVZBBvQ=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=mApYZsHGHZQZimnFDVO2wDv7a1+LY7xTj2VffkAb8T8AJ89+UwcsshiejaiGJtxVRU6n+WlO3AXwhzEPHesbEUaRDKDCqSvP7dSg/NXFi7eI/T0c0rBKPdPVKkQj7m5ldjU9RuJxYd4wprtg20M+EJleXHPZ4BUYNtBnDp+mUoE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=jnPMVMKS; arc=none smtp.client-ip=209.85.214.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="jnPMVMKS" Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2cc640dfde3so21273665ad.1 for ; Wed, 12 Aug 2026 17:26:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786580803; x=1787185603; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=cGhr1ahZhPKPx2jrgbs/gggrNdkMmSlAjSDp4bCUAbU=; b=jnPMVMKSihwH/IjyNiWmWz4SPkS/zCPPHjVhLyiiX8zbxrDvgAMEGJvy9dZbAMLKp/ KeMOkA54E2BihNyzaZJ9WmnZM07gwKr80QmI0qfStJ7ijcHXRPL//EPAUvkN8nuKtfcu o9HB/GHawvT65qTxWNlzP3HAlElrwAQZew0q+u0vZr0/xzpXNPzs7B0EZCiYj6Lr4Zvq NK5G2kdsTt2xVZ8c/febJDS5bi14L8uax+g0XFn91joEIJiYUe9zy/Edxz9eGZHWeVms /h9XG9KGzJUZ03aJWHd0DpqO2JppG3VgOGelkkrTO3FBRWZHo1PR7yEeGxO+XT3ZT0yu VwTQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786580803; x=1787185603; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=cGhr1ahZhPKPx2jrgbs/gggrNdkMmSlAjSDp4bCUAbU=; b=hqGapyUhv9+O/qz1OHCZWk6T1z3E3dm373t534cDIsqbcRU8Rv0tRh2BR/3wmdxdQ3 mrHlWgV7T6qVzY/iQaBwH9xK4BbwcnuJQO3oj2mNmkon3MvWWMP5w5TbzzrECHJhenwx KMeCvb6hw9WwH510PoT0IBtIBjBeyDBgnk5dtCBd9VgiapVAOyxXm5cBGmn2+9JPYHgW jAX7WQ+0VwuEZMMsRc7Yhf5PY63Ioo1EPAegd3qislj14yLd+EYNoaGaoNdZah5TC4vf tRwfEDko0jgJYnwW3QHJh9bFCFHmqwXBWdwRMG9JZTwRSyBpwU/JgODKORcJqsSsn+E1 aPiQ== X-Forwarded-Encrypted: i=1; AHgh+Rrx08M130v1frabv3AlDZlwwkp2dBPTotjBhxa8jQheb9gAt/8a1RvF5zASqMbwshVg86h4LzA3VNKLhAc=@vger.kernel.org X-Gm-Message-State: AOJu0YwL2oqGO83wMLEV1uLAe1dwLKzRCoNZCSG9Ab4DL3pE49k/QbV2 YHlkvqlt4Vbgtp9e2U4ugTJd1A71HqCtB165+XowWEQfXqUFNii1IgDqGho1gEGVn43wIYvxOC0 ChQ== X-Received: from plge13.prod.google.com ([2002:a17:902:cf4d:b0:2ca:f1f8:ea00]) (user=tweek job=prod-delivery.src-stubby-dispatcher) by 2002:a17:903:b88:b0:2cf:b68a:340 with SMTP id d9443c01a7336-2d37d884c75mr20405535ad.10.1786580802730; Wed, 12 Aug 2026 17:26:42 -0700 (PDT) Date: Thu, 13 Aug 2026 10:26:15 +1000 In-Reply-To: <20260813002618.3755631-1-tweek@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260813002618.3755631-1-tweek@google.com> X-Mailer: git-send-email 2.55.0.691.gc56d675ccc-goog Message-ID: <20260813002618.3755631-3-tweek@google.com> Subject: [PATCH bpf-next 2/5] bpf: Introduce BPF_LOADER_LOAD_FD command From: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" To: Paul Moore , Stephen Smalley , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Jeffrey Vander Stoep Cc: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" , Ondrej Mosnacek , Eric Suen , Blaise Boscaccy , Sid Nayyar , Neill Kapron , Eric Biggers , Greg Kroah-Hartman , KP Singh , bpf@vger.kernel.org, selinux@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Introduce the BPF_LOADER_LOAD_FD command to allow loading and executing loader BPF programs directly from an ELF file. This command implements the equivalent of bpf_load_and_run within the kernel. More specifically, it implements the four steps: 1. Create an array map 2. Populate the array with the loader data 3. Load the loader, using BPF_PROG_LOAD 4. Execute the loader using BPF_PROG_TEST_RUN BPF_LOADER_LOAD_FD takes 3 arguments, a file descriptor to an open ELF which contains the instructions, data and license of the loader; a context and its size which are passed to BPF_PROG_TEST_RUN. The kernel validates the ELF file and extracts three sections: 1. __loader.prog: Contains the loader instructions. 2. __loader.map: Contains the loader data. 3. license The caller is expected to use libbpf's light skeleton generator. The loader program is responsible for managing any maps or programs included in the original BPF object. CO-RE and BTF are explicitly not supported by the kernel here; they are handled by the loader directly. BPF_LOADER_LOAD_FD returns the updated context to userspace. The kernel treats this context as an opaque blob passed to BPF_PROG_TEST_RUN. For the userspace loader, this context typically contains the file descriptors of the newly created maps and programs. ELF validation borrows logic from the kernel module ELF validation in kernel/module/main.c. Signed-off-by: Thi=C3=A9baud Weksteen --- include/uapi/linux/bpf.h | 7 + kernel/bpf/syscall.c | 341 ++++++++++++++++++++++++++++++++- tools/include/uapi/linux/bpf.h | 7 + 3 files changed, 348 insertions(+), 7 deletions(-) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ffd96e8b920b..05b070a489fc 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -993,6 +993,7 @@ enum bpf_cmd { BPF_TOKEN_CREATE, BPF_PROG_STREAM_READ_BY_FD, BPF_PROG_ASSOC_STRUCT_OPS, + BPF_LOADER_LOAD_FD, __MAX_BPF_CMD, BPF_COMMON_ATTRS =3D 1 << 16, /* Indicate carrying syscall common attrs. = */ }; @@ -1950,6 +1951,12 @@ union bpf_attr { __u32 flags; } prog_assoc_struct_ops; =20 + struct { /* struct used by BPF_LOADER_LOAD_FD command */ + __u32 loader_fd; + __aligned_u64 ctx; + __u32 ctx_size; + } load_fd; + } __attribute__((aligned(8))); =20 /* The description below is an attempt at providing documentation to eBPF diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 8d111da88655..d79cd63f9f7c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -41,6 +41,7 @@ #include #include #include +#include =20 #include #include @@ -6291,6 +6292,336 @@ static int prog_assoc_struct_ops(union bpf_attr *at= tr) return ret; } =20 +#define BPF_LOADER_PROG_SEC "__loader.prog" +#define BPF_LOADER_MAP_SEC "__loader.map" +#define BPF_LOADER_LICENSE_SEC "license" +#define BPF_LOADER_MAX_SIZE (8U << 20) /* 8MB */ + +struct elf_info { + Elf64_Ehdr *hdr; + unsigned long len; + Elf64_Shdr *sechdrs; + char *secstrings; +}; + +static int bpf_validate_section_offset(const struct elf_info *info, Elf64_= Shdr *shdr) +{ + unsigned long long secend; + + /* + * Check for both overflow and offset/size being + * too large. + */ + secend =3D shdr->sh_offset + shdr->sh_size; + if (secend < shdr->sh_offset || secend > info->len) + return -ENOEXEC; + + return 0; +} + +static int bpf_elf_validity_ehdr(const struct elf_info *info) +{ + if (info->len < sizeof(*(info->hdr))) { + pr_err("Invalid ELF header len %lu\n", info->len); + return -ENOEXEC; + } + if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) !=3D 0) { + pr_err("Invalid ELF header magic: !=3D %s\n", ELFMAG); + return -ENOEXEC; + } + if (info->hdr->e_ident[EI_CLASS] !=3D ELFCLASS64) { + pr_err("Only 64-bit ELF is supported\n"); + return -ENOEXEC; + } + if (info->hdr->e_type !=3D ET_REL) { + pr_err("Invalid ELF header type: %u !=3D %u\n", + info->hdr->e_type, ET_REL); + return -ENOEXEC; + } + if (info->hdr->e_machine !=3D EM_BPF) { + pr_err("Invalid ELF machine type: %u !=3D %u\n", + info->hdr->e_machine, EM_BPF); + return -ENOEXEC; + } + return 0; +} + +static int bpf_elf_validity_cache_sechdrs(struct elf_info *info) +{ + Elf64_Shdr *sechdrs; + Elf64_Shdr *shdr; + int i; + int err; + + err =3D bpf_elf_validity_ehdr(info); + if (err < 0) + return err; + + if (info->hdr->e_shentsize !=3D sizeof(Elf64_Shdr)) { + pr_err("Invalid ELF section header size\n"); + return -ENOEXEC; + } + + /* + * e_shnum is 16 bits, and sizeof(Elf64_Shdr) is + * known and small. So e_shnum * sizeof(Elf64_Shdr) + * will not overflow unsigned long on any platform. + */ + if (info->hdr->e_shoff >=3D info->len + || (info->hdr->e_shnum * sizeof(Elf64_Shdr) > + info->len - info->hdr->e_shoff)) { + pr_err("Invalid ELF section header overflow\n"); + return -ENOEXEC; + } + + sechdrs =3D (void *)info->hdr + info->hdr->e_shoff; + + /* + * The code assumes that section 0 has a length of zero and + * an addr of zero, so check for it. + */ + if (sechdrs[0].sh_type !=3D SHT_NULL + || sechdrs[0].sh_size !=3D 0 + || sechdrs[0].sh_addr !=3D 0) { + pr_err("ELF Spec violation: section 0 type(%d)!=3DSH_NULL or non-zero le= n or addr\n", + sechdrs[0].sh_type); + return -ENOEXEC; + } + + /* Validate contents are inbounds */ + for (i =3D 1; i < info->hdr->e_shnum; i++) { + shdr =3D &sechdrs[i]; + switch (shdr->sh_type) { + case SHT_NULL: + case SHT_NOBITS: + /* No contents, offset/size don't mean anything */ + continue; + default: + err =3D bpf_validate_section_offset(info, shdr); + if (err < 0) { + pr_err("Invalid ELF section in BPF loader (section %u type %u)\n", + i, shdr->sh_type); + return err; + } + } + } + + info->sechdrs =3D sechdrs; + + return 0; +} + +static int bpf_elf_validity_cache_secstrings(struct elf_info *info) +{ + Elf64_Shdr *strhdr, *shdr; + char *secstrings; + int i; + + /* + * Verify if the section name table index is valid. + */ + if (info->hdr->e_shstrndx =3D=3D SHN_UNDEF + || info->hdr->e_shstrndx >=3D info->hdr->e_shnum) { + pr_err("Invalid ELF section name index: %d || e_shstrndx (%d) >=3D e_shn= um (%d)\n", + info->hdr->e_shstrndx, info->hdr->e_shstrndx, + info->hdr->e_shnum); + return -ENOEXEC; + } + + strhdr =3D &info->sechdrs[info->hdr->e_shstrndx]; + + if (strhdr->sh_type !=3D SHT_STRTAB) { + pr_err("Invalid ELF section name table type: %u\n", strhdr->sh_type); + return -ENOEXEC; + } + + /* + * The section name table must be NUL-terminated, as required + * by the spec. This makes strcmp and pr_* calls that access + * strings in the section safe. + */ + secstrings =3D (void *)info->hdr + strhdr->sh_offset; + if (strhdr->sh_size =3D=3D 0) { + pr_err("empty section name table\n"); + return -ENOEXEC; + } + if (secstrings[strhdr->sh_size - 1] !=3D '\0') { + pr_err("ELF Spec violation: section name table isn't null terminated\n"); + return -ENOEXEC; + } + + for (i =3D 0; i < info->hdr->e_shnum; i++) { + shdr =3D &info->sechdrs[i]; + /* SHT_NULL means sh_name has an undefined value */ + if (shdr->sh_type =3D=3D SHT_NULL) + continue; + if (shdr->sh_name >=3D strhdr->sh_size) { + pr_err("Invalid ELF section name in BPF loader (section %u type %u)\n", + i, shdr->sh_type); + return -ENOEXEC; + } + } + + info->secstrings =3D secstrings; + return 0; +} + +static int find_elf_section(const struct elf_info *info, + const char *sect_name, void **sect, int *sect_sz) +{ + Elf64_Shdr *shdr; + + for (int i =3D 1; i < info->hdr->e_shnum; i++) { + shdr =3D &info->sechdrs[i]; + if (shdr->sh_type =3D=3D SHT_NULL || shdr->sh_type =3D=3D SHT_NOBITS) + continue; + if (strcmp(sect_name, info->secstrings + shdr->sh_name) =3D=3D 0) { + *sect =3D (void *)info->hdr + shdr->sh_offset; + *sect_sz =3D shdr->sh_size; + return 0; + } + } + + return -EINVAL; +} + +/* To shut up -Wmissing-prototypes. + * This function is used by the kernel light skeleton + * to load bpf programs when modules are loaded or during kernel boot. + * See tools/lib/bpf/skel_internal.h + */ +int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size); + +#define BPF_LOADER_LOAD_FD_LAST_FIELD load_fd.ctx_size + +static int loader_load_fd(union bpf_attr *attr) +{ + void *buf =3D NULL, *insns =3D NULL, *data =3D NULL, *license =3D NULL; + void *kctx =3D NULL; + int len, err =3D 0; + int insns_sz =3D 0, data_sz =3D 0, license_sz =3D 0; + int map_fd, prog_fd; + size_t ctx_sz; + union bpf_attr sattr =3D { 0 }; + unsigned int zero =3D 0; + + if (!capable(CAP_BPF)) + return -EPERM; + + if (CHECK_ATTR(BPF_LOADER_LOAD_FD)) + return -EINVAL; + + if (attr->load_fd.ctx_size > U16_MAX) + return -EINVAL; + + CLASS(fd, f)(attr->load_fd.loader_fd); + if (fd_empty(f)) + return -EINVAL; + + len =3D kernel_read_file(fd_file(f), 0, &buf, BPF_LOADER_MAX_SIZE, NULL, + READING_BPF_LOADER); + if (len < 0) { + err =3D len; + goto out; + } + + struct elf_info elf_info =3D { + .hdr =3D (Elf64_Ehdr *) buf, + .len =3D len, + }; + + err =3D bpf_elf_validity_cache_sechdrs(&elf_info); + if (err) + goto out_free_buf; + + err =3D bpf_elf_validity_cache_secstrings(&elf_info); + if (err) + goto out_free_buf; + + err =3D find_elf_section(&elf_info, BPF_LOADER_PROG_SEC, &insns, &insns_s= z); + if (err) + goto out_free_buf; + + err =3D find_elf_section(&elf_info, BPF_LOADER_MAP_SEC, &data, &data_sz); + if (err) + goto out_free_buf; + + err =3D find_elf_section(&elf_info, BPF_LOADER_LICENSE_SEC, &license, &li= cense_sz); + if (err) + goto out_free_buf; + + if (license_sz =3D=3D 0 || ((char *)license)[license_sz - 1] !=3D '\0') { + pr_err("ELF Spec violation: license section isn't null terminated\n"); + err =3D -ENOEXEC; + goto out_free_buf; + } + + memset(&sattr, 0, sizeof(sattr)); + sattr.map_type =3D BPF_MAP_TYPE_ARRAY; + sattr.key_size =3D sizeof(unsigned int); + sattr.value_size =3D data_sz; + sattr.max_entries =3D 1; + map_fd =3D kern_sys_bpf(BPF_MAP_CREATE, &sattr, sizeof(sattr)); + if (map_fd < 0) { + err =3D map_fd; + goto out_free_buf; + } + + memset(&sattr, 0, sizeof(sattr)); + sattr.map_fd =3D map_fd; + sattr.key =3D (unsigned long) &zero; + sattr.value =3D (unsigned long) data; + err =3D kern_sys_bpf(BPF_MAP_UPDATE_ELEM, &sattr, sizeof(sattr)); + if (err < 0) + goto close_map_err; + + memset(&sattr, 0, sizeof(sattr)); + sattr.prog_type =3D BPF_PROG_TYPE_SYSCALL; + sattr.license =3D (unsigned long) license; + sattr.insns =3D (unsigned long) insns; + sattr.insn_cnt =3D insns_sz / sizeof(struct bpf_insn); + sattr.fd_array =3D (unsigned long) &map_fd; + sattr.prog_flags =3D BPF_F_SLEEPABLE; + strscpy(sattr.prog_name, BPF_LOADER_PROG_SEC, sizeof(BPF_LOADER_PROG_SEC)= ); + prog_fd =3D kern_sys_bpf(BPF_PROG_LOAD, &sattr, sizeof(sattr)); + if (prog_fd < 0) { + err =3D prog_fd; + goto close_map_err; + } + + memset(&sattr, 0, sizeof(sattr)); + ctx_sz =3D attr->load_fd.ctx_size; + kctx =3D kzalloc(ctx_sz, GFP_KERNEL); + if (kctx =3D=3D NULL) { + err =3D -ENOMEM; + goto close_prog_err; + } + sattr.test.prog_fd =3D prog_fd; + sattr.test.ctx_in =3D (unsigned long) kctx; + sattr.test.ctx_size_in =3D ctx_sz; + err =3D kern_sys_bpf(BPF_PROG_TEST_RUN, &sattr, sizeof(sattr)); + if (err < 0) + goto free_ctx; + err =3D sattr.test.retval; + if (err < 0) + goto free_ctx; + + if (copy_to_user((void *) attr->load_fd.ctx, kctx, ctx_sz) !=3D 0) + err =3D -EFAULT; + +free_ctx: + kfree(kctx); +close_prog_err: + close_fd(prog_fd); +close_map_err: + close_fd(map_fd); +out_free_buf: + vfree(buf); +out: + return err; +} + + static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr, unsigned int size, bpfptr_t uattr_common, unsigned int size_common) { @@ -6463,6 +6794,9 @@ static int __sys_bpf(enum bpf_cmd cmd, bpfptr_t uattr= , unsigned int size, case BPF_PROG_ASSOC_STRUCT_OPS: err =3D prog_assoc_struct_ops(&attr); break; + case BPF_LOADER_LOAD_FD: + err =3D loader_load_fd(&attr); + break; default: err =3D -EINVAL; break; @@ -6508,13 +6842,6 @@ BPF_CALL_3(bpf_sys_bpf, int, cmd, union bpf_attr *, = attr, u32, attr_size) } =20 =20 -/* To shut up -Wmissing-prototypes. - * This function is used by the kernel light skeleton - * to load bpf programs when modules are loaded or during kernel boot. - * See tools/lib/bpf/skel_internal.h - */ -int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size); - int kern_sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) { struct bpf_prog * __maybe_unused prog; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index ffd96e8b920b..470e3b575497 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -993,6 +993,7 @@ enum bpf_cmd { BPF_TOKEN_CREATE, BPF_PROG_STREAM_READ_BY_FD, BPF_PROG_ASSOC_STRUCT_OPS, + BPF_LOADER_LOAD_FD, __MAX_BPF_CMD, BPF_COMMON_ATTRS =3D 1 << 16, /* Indicate carrying syscall common attrs. = */ }; @@ -1950,6 +1951,12 @@ union bpf_attr { __u32 flags; } prog_assoc_struct_ops; =20 + struct { /* struct used by BPF_LOADER_LOAD_FD command */ + __u32 loader_fd; + __aligned_u64 ctx; + __u32 ctx_size; + } load_fd; + } __attribute__((aligned(8))); =20 /* The description below is an attempt at providing documentation to eBPF --=20 2.55.0.691.gc56d675ccc-goog From nobody Tue Sep 29 04:09:42 2026 Received: from mail-pf1-f199.google.com (mail-pf1-f199.google.com [209.85.210.199]) (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 3CE172ED154 for ; Thu, 13 Aug 2026 00:26:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.199 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580809; cv=none; b=QiSVWwfC8I9bG1ayWzCQIS2ml+ZKYJzdKibr7/Wlq8TYVJ8TnzFiM/yrqAo2vL8cwq9Ak0pgcu1Qi7d66UCdF6ArQeLkoIyls3hjjGQ85o66bCYAu/JaGK5AkJDlC6iz2GBbvwvkI5MjRWrtjQuCuHniw354aZqakqnoPGdsk+I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580809; c=relaxed/simple; bh=5se711kePA411xaoz5RodsO+PLdGel9RlS6q8G/sC4I=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=bFkuQFW6m8fBH6Fvt82mpeQNoLph/w0aU1rAuVoAe9q1I22KEL8JgmBlchOx3VBmUbccJTqcRiAE9ktapC4Lcb/83BfJz3COEmWFW/ZdhDbmNcnObKAdvSizk5OKnCkPmPF1gw0vWOgTgNbGpZkgYj2j3h3PeQ4UgyBytDwKx5c= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=PLPeZ39k; arc=none smtp.client-ip=209.85.210.199 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="PLPeZ39k" Received: by mail-pf1-f199.google.com with SMTP id d2e1a72fcca58-848662cd2a1so1859223b3a.2 for ; Wed, 12 Aug 2026 17:26:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786580807; x=1787185607; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=Qkjz+h+zNCGoWltKJetL2Y2JT1Z7HAkSp1FmTaM3CtU=; b=PLPeZ39kIdPQ9wekhs0NzjRWi0g5wU9Prl+htO8xBcFkU3JCSA1SE+liU26MJMGsE5 ByhTiXnyRWgD59RcAct4IrEAjcZoPU7NR2PqLOLU8tlCk4OOIG/1EBit6OoqTy1GFSFm H2OcpN+BH4NmrPKnvdV88+SKddhxHQkku6jYjGkny99+NX2knla7Q0mmxyARdUr6oBM6 E7jWGqVo7QBmfMo3FBp+I8LOM8XjJG3eIN8tFwTNCc1CD95G1GYCIEbsxN0gzpF3kS20 9ippzqsPZ2zOFqmAs8+5bVijVypsJbv8Vhx7af4d049+yxa3orpHGPpG0zuUakufFLSD CHng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786580807; x=1787185607; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Qkjz+h+zNCGoWltKJetL2Y2JT1Z7HAkSp1FmTaM3CtU=; b=Tg3Nn6A0TFLqgyAlJlb2B9wwuO9yCEhb37bnklmhTrsU5gEZnKqpJ1W371tN8mhIRe xHaGzPNTTpZ5hqKLXx2nznwcL4j9uCQlDSbZK1krZiAml9tF0ipDPaT7C+d5guXsRhwE u1QSphKx2PxVSmciT98p3Ks/KKkmYiUmLZnCvGqvMJdeRbyoP9iecSSOWxiQZ5CdqgMC QXBeHrpGiOXhPdfvtJmOBCNicC/B8+GhL5XvL7I6B6+ZiatkFSHPW/mREnDoI/+wl6TI k1boZRYhp/StiwRXGtSFnLkmg+XWsiSMWQHqIchZ56p0+JX1BhEfCjC1k05UbIfY8EVj NhDg== X-Forwarded-Encrypted: i=1; AHgh+RoZhSgZkAh4suAmi4vfetkhl1BbcFMrngAax6t475ifLjQsrtKK7dQsi+gDIN4zumK3ej4VLRcuqi+sRXs=@vger.kernel.org X-Gm-Message-State: AOJu0YzywPAVwm6P3Z4nXClFgZalTbyBk+Q3Y/VnpuSgITuqYUTqsXfq Ji0/t7E+ivmIRB/KIlN7E8r76+b9dTbuvO9X/veWe8ffmscwUucsucRQbFQI5qi09PAMMZOAfpE LtA== X-Received: from pfx22.prod.google.com ([2002:a05:6a00:a456:b0:84b:50b5:d431]) (user=tweek job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a00:44cb:b0:848:4080:afe8 with SMTP id d2e1a72fcca58-84fc751547fmr1831794b3a.22.1786580807349; Wed, 12 Aug 2026 17:26:47 -0700 (PDT) Date: Thu, 13 Aug 2026 10:26:16 +1000 In-Reply-To: <20260813002618.3755631-1-tweek@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260813002618.3755631-1-tweek@google.com> X-Mailer: git-send-email 2.55.0.691.gc56d675ccc-goog Message-ID: <20260813002618.3755631-4-tweek@google.com> Subject: [PATCH bpf-next 3/5] selinux: use kernel sid in security_bpf_* From: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" To: Paul Moore , Stephen Smalley , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Jeffrey Vander Stoep Cc: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" , Ondrej Mosnacek , Eric Suen , Blaise Boscaccy , Sid Nayyar , Neill Kapron , Eric Biggers , Greg Kroah-Hartman , KP Singh , bpf@vger.kernel.org, selinux@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The security_bpf hooks provides a boolean to indicate if the call is coming from within the kernel or not. If true, use the kernel SID instead of relying on the current process SID. For the token-aware functions, the kernel sid is used to decide on the access, but the caller remains owner of the object (program or map). Signed-off-by: Thi=C3=A9baud Weksteen --- security/selinux/hooks.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f197cf476190..e7c5993f6954 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7181,7 +7181,7 @@ static int selinux_ib_alloc_security(void *ib_sec) static int selinux_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel) { - u32 sid =3D current_sid(); + u32 sid =3D kernel ? SECINITSID_KERNEL : current_sid(); int ret; =20 if (selinux_policycap_bpf_token_perms()) @@ -7296,7 +7296,7 @@ static int selinux_bpf_map_create(struct bpf_map *map= , union bpf_attr *attr, bpfsec->sid =3D current_sid(); =20 if (!token) - ssid =3D bpfsec->sid; + ssid =3D kernel ? SECINITSID_KERNEL : bpfsec->sid; else ssid =3D selinux_bpffs_creator_sid(attr->map_token_fd); =20 @@ -7314,7 +7314,7 @@ static int selinux_bpf_prog_load(struct bpf_prog *pro= g, union bpf_attr *attr, bpfsec->sid =3D current_sid(); =20 if (!token) - ssid =3D bpfsec->sid; + ssid =3D kernel ? SECINITSID_KERNEL : bpfsec->sid; else ssid =3D selinux_bpffs_creator_sid(attr->prog_token_fd); =20 --=20 2.55.0.691.gc56d675ccc-goog From nobody Tue Sep 29 04:09:42 2026 Received: from mail-pg1-f200.google.com (mail-pg1-f200.google.com [209.85.215.200]) (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 A17B82FFDD5 for ; Thu, 13 Aug 2026 00:26:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.200 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580812; cv=none; b=RNJZaJwNqWV72MFWVOA/7R7UomMeJ2bwSMcXst9+1pXqZFS0Izky9OTpkIAEY1hLSFrimvnli9flhKQYKCCXCGrhGtUpw327achpIShaSNLTyBLnd8H2JzrcIrZe8oFpHOMZcdPiLK7epVO0XPsGTt/cds7Yey+MJaMPXelsSLg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580812; c=relaxed/simple; bh=veTqDKWVWhuIMMVDuiWZUa0YQ97OCY9TCpyTNIzkWzs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=VPxd2A/rtQ0lzey4zpHElzvjXQaERlRqDdw6AwsXd02xL/l+QMyyYBLBEXJdCpyQ/Tq2LG2ExtF/ad8Q1Dj/br3Ix7NwOnPz29t9+Cxs9c8HYDLCjzI1xR+8anjzjrmajSxOoQoHOvKVRCa3cAq276FJHq6VZ6d8rm5jMehjnhs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=BpGa6Uw+; arc=none smtp.client-ip=209.85.215.200 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="BpGa6Uw+" Received: by mail-pg1-f200.google.com with SMTP id 41be03b00d2f7-cbee5bab340so1762108a12.2 for ; Wed, 12 Aug 2026 17:26:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786580811; x=1787185611; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=MCduZZ2cuHkGfG92sUop8F3LbOgQDBWvf7V8w/75FyY=; b=BpGa6Uw+JcqlAX/GhW4ZSNPN+9v2CEFpjqNXk0pGEXz6J+uuTcwqCHg9/ElHQpJi3c GDG0+IVoRU4qrKKjrdWPaS3Wr36fe0kQVL3ULLZ3B4yfDVy1I2ZZonLpi61GfG4HmzcW kDSf0+91GW1oa3JydlwgbtsqPPqjhZT+gQwf0aBFyFq1Z7+23zziJ49UN0+8+wB0C/t8 RZ/Cjr/lPP5IYsWAjpZEKu5t09Y2xlNU0Y3MHJ2W0X1c1RUOXx8oJobQL6nshOX+c65w xnFHAfGZNi9iUw1iLx6MhEJAm5BZALdEIiVU45+yf5vq6onxhjjKbATpT4UMXxgrT9wX G45g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786580811; x=1787185611; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=MCduZZ2cuHkGfG92sUop8F3LbOgQDBWvf7V8w/75FyY=; b=dwVKSGXHsQhvPUTqgDp95PWNFQzSwmdP59ZiPVXjmJtuCGcy/ek1/mPXZFXs8OGwxL lbw61Y1TPHy8Oy82m7kI7kjdwBHFF/5/TOdqp0c4bWcZXRVaZYJN3I9PBkdtY9SGspSB qzPfwcjkZ9aSTwHlHBNhB4WpopZqh7WDBshKmOQFUi9IAXniEhfeFG0g4g1c+zay65dI lOFMlcshkbprEPvnvRGdMXbZ9uCqByAz95wcstngZQH6CtmUhPTVwnpY4sM8H1pu5OlQ N3wdFvHrBKhxeQbV2LSvOBHfHCKkkW3tzBGAtq5ViJdWBq6ZogdP63RlmPdJBuqZXJ0G S4NQ== X-Forwarded-Encrypted: i=1; AHgh+RowXjX9DXw2jc08YvEIdwrq549BJm41xysui/Yj2mPCQkylY1QPjkgPe837HWYqoksQ9sukTce+TMcQTeY=@vger.kernel.org X-Gm-Message-State: AOJu0Yx/8rDpikYGuVa4M0XzusWgSFijdeRtA3+sZx7NDdR7zOixTlEH c6zQ9Q9FBXoubNmpuLtWe7zyS0X7vd8FqdUFtzQo1w+8wf0fotk916E7J3+J9RQpk9842wgWKxJ EyA== X-Received: from dyw11.prod.google.com ([2002:a05:7300:880b:b0:313:afba:b76]) (user=tweek job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a21:9211:b0:3c4:397a:69b8 with SMTP id adf61e73a8af0-3cc5539bfb4mr3469072637.21.1786580810724; Wed, 12 Aug 2026 17:26:50 -0700 (PDT) Date: Thu, 13 Aug 2026 10:26:17 +1000 In-Reply-To: <20260813002618.3755631-1-tweek@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260813002618.3755631-1-tweek@google.com> X-Mailer: git-send-email 2.55.0.691.gc56d675ccc-goog Message-ID: <20260813002618.3755631-5-tweek@google.com> Subject: [PATCH bpf-next 4/5] selinux: Add BPF_LOADER_LOAD_FD syscall permission From: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" To: Paul Moore , Stephen Smalley , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Jeffrey Vander Stoep Cc: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" , Ondrej Mosnacek , Eric Suen , Blaise Boscaccy , Sid Nayyar , Neill Kapron , Eric Biggers , Greg Kroah-Hartman , KP Singh , bpf@vger.kernel.org, selinux@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the BPF_LOADER_LOAD_FD permission to gate the bpf syscall command of the same name. Signed-off-by: Thi=C3=A9baud Weksteen --- security/selinux/hooks.c | 4 ++++ security/selinux/include/classmap.h | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e7c5993f6954..b4ff5ea5306d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -7196,6 +7196,10 @@ static int selinux_bpf(int cmd, union bpf_attr *attr, ret =3D avc_has_perm(sid, sid, SECCLASS_BPF, BPF__PROG_LOAD, NULL); break; + case BPF_LOADER_LOAD_FD: + ret =3D avc_has_perm(sid, sid, SECCLASS_BPF, BPF__LOADER_LOAD_FD, + NULL); + break; default: ret =3D 0; break; diff --git a/security/selinux/include/classmap.h b/security/selinux/include= /classmap.h index 453522ca87df..4c6cc71b233c 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -171,7 +171,7 @@ const struct security_class_mapping secclass_map[] =3D { { "infiniband_endport", { "manage_subnet", NULL } }, { "bpf", { "map_create", "map_read", "map_write", "prog_load", "prog_run", - "map_create_as", "prog_load_as", NULL } }, + "map_create_as", "prog_load_as", "loader_load_fd", NULL } }, { "xdp_socket", { COMMON_SOCK_PERMS, NULL } }, { "mctp_socket", { COMMON_SOCK_PERMS, NULL } }, { "perf_event", --=20 2.55.0.691.gc56d675ccc-goog From nobody Tue Sep 29 04:09:42 2026 Received: from mail-pl1-f198.google.com (mail-pl1-f198.google.com [209.85.214.198]) (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 E3C5330569F for ; Thu, 13 Aug 2026 00:26:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.198 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580817; cv=none; b=CrotyMW8v0Zg/hUa6GFa0h6RaL3l8HMJuj7Vqid+aYJX4z7J6936pRE0HW+vX8+O/+2v/6c/iTqtfwSlsxxXI4X+8bLcZScVME1FTtoNExCZAp6lZcvsh8xg65FIgvBkDlFmzMmvcnyPUSHsUHvmMcEqSQEdhO0jFAKyV/6/j4s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786580817; c=relaxed/simple; bh=ocN550/VLv+O+BfqZpdudHpDqckAILcNvv3v8RqAZrI=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=py1uLL44PPoxtFxxlIsLAIhvQXrROmSKtBA1kq5x7Oi/YtFntXgMuu73Mxp5bZad7bN5harBOeYnNTsawt0lK77hmcmgWVJJ3c14OpkSdi0N3dfQMTjJqJFbY7JKwM8pqIv2w2wGidL6gKY6THUrgxrpbCQ/X46PoPTWolKnqn4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=pSkYcYW/; arc=none smtp.client-ip=209.85.214.198 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--tweek.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="pSkYcYW/" Received: by mail-pl1-f198.google.com with SMTP id d9443c01a7336-2cc88e22f92so4542725ad.1 for ; Wed, 12 Aug 2026 17:26:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1786580814; x=1787185614; darn=vger.kernel.org; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date:from:to:cc :subject:date:message-id:reply-to:content-type; bh=Ov/Vpx89xx+37QG1bG7v8iJU4ZfOlg8N8RktsscspuE=; b=pSkYcYW/mjoT83TuoBEPQhj6F+nYD8Kq3WCWBxvfN0LeiSqUq5TonX93eZKoPZE6ii u/JGlMgasZARVcvc4/BvDSnabv8iHhLNg7pazfyqDUbJ74tlbR5RkBP/Djg02X7JErBv HlFWPobEbKd0LopmCk8UuWiuTD04huJVzk/kJDU6ycFtXGxTEbcOskVfNXFbD4wADqSm VXchUQuqayFl6PlVYVmcQdw+6HiqvQY/ypH/6Oy2IBJ0iMVsm5hPS8U2xpVmgtBGZBKF E94lI8ykNjEhoRnVy5hkzpbuMxw4JGsmU19P51zgtUjvzrD8ogynC9tb+q+vIQkp0HQ0 qOjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1786580814; x=1787185614; h=content-transfer-encoding:content-type:cc:to:from:subject :message-id:references:mime-version:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Ov/Vpx89xx+37QG1bG7v8iJU4ZfOlg8N8RktsscspuE=; b=JAe0ufXehODCLn9fyHHghZJ2c+ZRpP1+oAonU2Xd7Gg28UY0B2G0BFud7tkVknYFuP zO9pAlad9sJc6GGn6T3i4Ah/kyKSiG8p6cD2pQJ5Vsv+Hgrxwbqmxc8vAkKIJO0PPWrZ DAa/Uu+1kgptAUyW7u38OSP8BVRWi4YpiQohBrDh3Doo+ePmHC8xydjcH2DR/Ut5VI0q zT1Kw0EKgNspbI5h3LTd4ThSwXW6VSij63J20uCtJlFx6whx/VsIuBbmjZtupZs8qGFT H5Tvn84J3A5pdbpYzxFRmR3POXCEcNkIF+8/jvlMb/kWaId1KFCG7e4SJU3xK5WtwMhA QgKA== X-Forwarded-Encrypted: i=1; AHgh+RqVmMtyW8uqdDYxy8AQTjBwQbVXW1Ttf2BzdcgFchxbOWHdx5LsBSRNXooV5tfhjoPIPIm5gtrINKMJAYg=@vger.kernel.org X-Gm-Message-State: AOJu0YyDjQBVP2g4tS3rh9lVc4jWQc8BOccYzRIfdQQK/XC+prvLi0g5 38hbXV/vAf+HW2Uiz+Zormz7ZTPjKmyEMwj/toA3tGRktqOG9PJ7Pbf9g7acgYnYKytNIIw6gq4 gRA== X-Received: from plhi8.prod.google.com ([2002:a17:903:2ec8:b0:2cf:7e98:1aee]) (user=tweek job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:f544:b0:2d0:cc92:f7b8 with SMTP id d9443c01a7336-2d37d53b01amr16781025ad.2.1786580814030; Wed, 12 Aug 2026 17:26:54 -0700 (PDT) Date: Thu, 13 Aug 2026 10:26:18 +1000 In-Reply-To: <20260813002618.3755631-1-tweek@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260813002618.3755631-1-tweek@google.com> X-Mailer: git-send-email 2.55.0.691.gc56d675ccc-goog Message-ID: <20260813002618.3755631-6-tweek@google.com> Subject: [PATCH bpf-next 5/5] selftests/bpf: add loader_load_fd tests From: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" To: Paul Moore , Stephen Smalley , Alexei Starovoitov , Daniel Borkmann , Andrii Nakryiko , Jeffrey Vander Stoep Cc: "=?UTF-8?q?Thi=C3=A9baud=20Weksteen?=" , Ondrej Mosnacek , Eric Suen , Blaise Boscaccy , Sid Nayyar , Neill Kapron , Eric Biggers , Greg Kroah-Hartman , KP Singh , bpf@vger.kernel.org, selinux@vger.kernel.org, linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add user-space selftests for BPF_LOADER_LOAD_FD command. The test ELF is generated based on the existing light skeleton generator. An awk script extracts the loader program and map. In the future, it is possible to add an extra option to `bpftool gen skeleton` to output the ELF file directly. Signed-off-by: Thi=C3=A9baud Weksteen --- tools/testing/selftests/bpf/Makefile | 8 +- tools/testing/selftests/bpf/loader_setup.sh | 68 ++++ .../selftests/bpf/prog_tests/loader_load_fd.c | 361 ++++++++++++++++++ .../testing/selftests/bpf/progs/test_loader.c | 21 + 4 files changed, 456 insertions(+), 2 deletions(-) create mode 100755 tools/testing/selftests/bpf/loader_setup.sh create mode 100644 tools/testing/selftests/bpf/prog_tests/loader_load_fd.c create mode 100644 tools/testing/selftests/bpf/progs/test_loader.c diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests= /bpf/Makefile index d3655a706482..6d881584abbc 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -525,7 +525,7 @@ LINKED_SKELS :=3D test_static_linked.skel.h linked_func= s.skel.h \ LSKELS :=3D fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \ core_kern.c core_kern_overflow.c test_ringbuf.c \ test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c \ - test_ringbuf_overwrite.c + test_ringbuf_overwrite.c test_loader.c =20 LSKELS_SIGNED :=3D fentry_test.c fexit_test.c atomics.c =20 @@ -577,7 +577,8 @@ TRUNNER_EXTRA_OBJS :=3D $$(patsubst %.c,$$(TRUNNER_OUTP= UT)/%.o, \ $$(filter %.c,$(TRUNNER_EXTRA_SOURCES))) TRUNNER_LIB_OBJS :=3D $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.o, \ $$(filter %.c,$(TRUNNER_LIB_SOURCES))) -TRUNNER_EXTRA_HDRS :=3D $$(filter %.h,$(TRUNNER_EXTRA_SOURCES)) +TRUNNER_EXTRA_HDRS :=3D $$(filter %.h,$(TRUNNER_EXTRA_SOURCES)) $$(TRUNNER= _OUTPUT)/test_loader_processed.lskel.h + TRUNNER_TESTS_HDR :=3D $(TRUNNER_TESTS_DIR)/tests.h TRUNNER_BPF_SRCS :=3D $$(notdir $$(wildcard $(TRUNNER_BPF_PROGS_DIR)/*.c)) TRUNNER_BPF_OBJS :=3D $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.bpf.o, $$(TRUNN= ER_BPF_SRCS)) @@ -663,6 +664,9 @@ $(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | = $(TRUNNER_OUTPUT) }) && \ rm -f $$(<:.o=3D.llinked1.o) $$(<:.o=3D.llinked2.o) $$(<:.o=3D.llinked3.o) =20 +$$(TRUNNER_OUTPUT)/test_loader_processed.lskel.h: $$(TRUNNER_OUTPUT)/test_= loader.lskel.h loader_setup.sh + $$(Q)./loader_setup.sh $$< $$@ + $(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OU= TPUT) $(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \ $$(RM) $$@; \ diff --git a/tools/testing/selftests/bpf/loader_setup.sh b/tools/testing/se= lftests/bpf/loader_setup.sh new file mode 100755 index 000000000000..f86651b0718f --- /dev/null +++ b/tools/testing/selftests/bpf/loader_setup.sh @@ -0,0 +1,68 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2026 Google LLC +# +# loader_setup.sh - Light skeleton data extraction helper for selftests/bpf +# +# This script parses an autogenerated light skeleton header (.lskel.h) pro= duced +# by bpftool ('bpftool gen skeleton -L'). It extracts the embedded loader = data +# ('opts_data') and loader BPF instructions ('opts_insn') string literals = and +# generates a C header file containing: +# - loader_test_opts_data[]: Data for the '__loader.map' section. +# - loader_test_opts_data_sz: Size of opts_data. +# - loader_test_opts_insn[]: Instructions for the '__loader.prog' sectio= n. +# - loader_test_opts_insn_sz: Size of opts_insn. +# +# These extracted sections are used by the 'loader_load_fd' selftest to po= pulate +# an in-memory ELF object file for kernel 'BPF_LOADER_LOAD_FD' testing. +# +# Usage: +# loader_setup.sh + +INPUT=3D"$1" +OUTPUT=3D"$2" + +if [ -z "$INPUT" ] || [ -z "$OUTPUT" ]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +awk -v input=3D"$INPUT" ' +BEGIN { + print "/* Generated from " input " */" + in_data =3D 0 + in_insn =3D 0 +} +/opts_data\[\]/ { + in_data =3D 1 + sub(/^.*opts_data\[\][^=3D"]*=3D\s*"/, "") + printf "static const char test_loader_opts_data[] __attribute__((__aligne= d__(8))) =3D \"" +} +in_data { + if (index($0, "\";") > 0) { + sub(/";.*/, "") + print $0 "\";" + print "static const size_t test_loader_opts_data_sz =3D " \ + "sizeof(test_loader_opts_data) - 1;\n" + in_data =3D 0 + } else { + print $0 + } +} +/opts_insn\[\]/ { + in_insn =3D 1 + sub(/^.*opts_insn\[\][^=3D"]*=3D\s*"/, "") + printf "static const char test_loader_opts_insn[] __attribute__((__aligne= d__(8))) =3D \"" +} +in_insn { + if (index($0, "\";") > 0) { + sub(/";.*/, "") + print $0 "\";" + print "static const size_t test_loader_opts_insn_sz =3D " \ + "sizeof(test_loader_opts_insn) - 1;" + in_insn =3D 0 + } else { + print $0 + } +} +' "$INPUT" > "$OUTPUT" diff --git a/tools/testing/selftests/bpf/prog_tests/loader_load_fd.c b/tool= s/testing/selftests/bpf/prog_tests/loader_load_fd.c new file mode 100644 index 000000000000..ab971662dd04 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/loader_load_fd.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2026 Google LLC */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bpf/skel_internal.h" +#include "test_loader_processed.lskel.h" + +/* Test helper to create an in-memory ELF */ +static int create_loader_elf(const void *insns, size_t insns_sz, + const void *map_data, size_t map_data_sz, + const char *license, size_t license_sz, + bool omit_prog, bool omit_map, bool omit_license) +{ + char shstrtab[] =3D "\0__loader.prog\0__loader.map\0license\0.shstrtab"; + size_t prog_off =3D 1; + size_t map_off =3D prog_off + strlen("__loader.prog") + 1; + size_t lic_off =3D map_off + strlen("__loader.map") + 1; + size_t shstr_off =3D lic_off + strlen("license") + 1; + size_t shstrtab_sz =3D sizeof(shstrtab); + Elf_Data *shstr_data, *data; + Elf64_Shdr *shstr_shdr, *shdr; + Elf_Scn *shstr_scn, *scn; + Elf64_Ehdr *ehdr; + Elf *elf; + int fd; + + fd =3D memfd_create("loader_elf", 0); + if (!ASSERT_GE(fd, 0, "memfd_create")) + return -1; + + elf_version(EV_CURRENT); + elf =3D elf_begin(fd, ELF_C_WRITE, NULL); + if (!ASSERT_OK_PTR(elf, "elf_begin")) { + close(fd); + return -1; + } + + ehdr =3D elf64_newehdr(elf); + if (!ASSERT_OK_PTR(ehdr, "elf64_newehdr")) + goto err; + + ehdr->e_ident[EI_MAG0] =3D ELFMAG0; + ehdr->e_ident[EI_MAG1] =3D ELFMAG1; + ehdr->e_ident[EI_MAG2] =3D ELFMAG2; + ehdr->e_ident[EI_MAG3] =3D ELFMAG3; + ehdr->e_ident[EI_CLASS] =3D ELFCLASS64; + ehdr->e_ident[EI_DATA] =3D ELFDATA2LSB; + ehdr->e_ident[EI_VERSION] =3D EV_CURRENT; + ehdr->e_machine =3D EM_BPF; + ehdr->e_type =3D ET_REL; + ehdr->e_version =3D EV_CURRENT; + + shstr_scn =3D elf_newscn(elf); + shstr_shdr =3D elf64_getshdr(shstr_scn); + shstr_shdr->sh_name =3D shstr_off; + shstr_shdr->sh_type =3D SHT_STRTAB; + shstr_shdr->sh_flags =3D 0; + + shstr_data =3D elf_newdata(shstr_scn); + shstr_data->d_buf =3D shstrtab; + shstr_data->d_size =3D shstrtab_sz; + shstr_data->d_type =3D ELF_T_BYTE; + shstr_data->d_align =3D 1; + + ehdr->e_shstrndx =3D elf_ndxscn(shstr_scn); + + if (!omit_prog && insns && insns_sz > 0) { + scn =3D elf_newscn(elf); + shdr =3D elf64_getshdr(scn); + shdr->sh_name =3D prog_off; + shdr->sh_type =3D SHT_PROGBITS; + shdr->sh_flags =3D SHF_ALLOC | SHF_EXECINSTR; + + data =3D elf_newdata(scn); + data->d_buf =3D (void *)insns; + data->d_size =3D insns_sz; + data->d_type =3D ELF_T_BYTE; + data->d_align =3D 8; + } + + if (!omit_map && map_data && map_data_sz > 0) { + scn =3D elf_newscn(elf); + shdr =3D elf64_getshdr(scn); + shdr->sh_name =3D map_off; + shdr->sh_type =3D SHT_PROGBITS; + shdr->sh_flags =3D SHF_ALLOC; + + data =3D elf_newdata(scn); + data->d_buf =3D (void *)map_data; + data->d_size =3D map_data_sz; + data->d_type =3D ELF_T_BYTE; + data->d_align =3D 8; + } + + if (!omit_license && license && license_sz > 0) { + scn =3D elf_newscn(elf); + shdr =3D elf64_getshdr(scn); + shdr->sh_name =3D lic_off; + shdr->sh_type =3D SHT_PROGBITS; + shdr->sh_flags =3D 0; + + data =3D elf_newdata(scn); + data->d_buf =3D (void *)license; + data->d_size =3D license_sz; + data->d_type =3D ELF_T_BYTE; + data->d_align =3D 1; + } + + if (elf_update(elf, ELF_C_WRITE) < 0) + goto err; + + elf_end(elf); + lseek(fd, 0, SEEK_SET); + return fd; + +err: + elf_end(elf); + close(fd); + return -1; +} + +static int sys_bpf_loader_load_fd(int loader_fd, void *ctx, __u32 ctx_size) +{ + union bpf_attr attr; + + memset(&attr, 0, sizeof(attr)); + attr.load_fd.loader_fd =3D loader_fd; + attr.load_fd.ctx =3D ptr_to_u64(ctx); + attr.load_fd.ctx_size =3D ctx_size; + + return syscall(__NR_bpf, BPF_LOADER_LOAD_FD, &attr, sizeof(attr)); +} + +static void test_loader_load_fd_invalid_fd(void) +{ + struct bpf_loader_ctx ctx =3D {}; + int err; + + err =3D sys_bpf_loader_load_fd(-1, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "invalid fd sys_bpf return"); + ASSERT_EQ(errno, EINVAL, "invalid fd errno"); +} + +static void test_loader_load_fd_oversized_ctx(void) +{ + struct bpf_insn insns[] =3D { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + char map_data[] =3D "data"; + char license[] =3D "GPL"; + struct bpf_loader_ctx ctx =3D {}; + int elf_fd, err; + + elf_fd =3D create_loader_elf(insns, sizeof(insns), + map_data, sizeof(map_data), + license, sizeof(license), + false, false, false); + + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &ctx, 65536U); + ASSERT_EQ(err, -1, "oversized ctx sys_bpf return"); + ASSERT_EQ(errno, EINVAL, "oversized ctx errno"); + + close(elf_fd); +} + +static void test_loader_load_fd_invalid_elf(void) +{ + char garbage[] =3D "not_an_elf_file_content"; + struct bpf_loader_ctx ctx =3D {}; + int fd, err; + + fd =3D memfd_create("garbage_file", 0); + if (!ASSERT_GE(fd, 0, "memfd_create")) + return; + + if (!ASSERT_EQ(write(fd, garbage, sizeof(garbage)), sizeof(garbage), "wri= te garbage")) { + close(fd); + return; + } + lseek(fd, 0, SEEK_SET); + + err =3D sys_bpf_loader_load_fd(fd, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "invalid elf sys_bpf return"); + ASSERT_EQ(errno, ENOEXEC, "invalid elf errno"); + + close(fd); +} + +static void test_loader_load_fd_missing_prog_sec(void) +{ + struct bpf_insn insns[] =3D { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + char map_data[] =3D "data"; + char license[] =3D "GPL"; + struct bpf_loader_ctx ctx =3D {}; + int elf_fd, err; + + elf_fd =3D create_loader_elf(insns, sizeof(insns), + map_data, sizeof(map_data), + license, sizeof(license), + true, false, false); + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "missing prog sec sys_bpf return"); + ASSERT_EQ(errno, EINVAL, "missing prog sec errno"); + + close(elf_fd); +} + +static void test_loader_load_fd_missing_map_sec(void) +{ + struct bpf_insn insns[] =3D { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + char map_data[] =3D "data"; + char license[] =3D "GPL"; + struct bpf_loader_ctx ctx =3D {}; + int elf_fd, err; + + elf_fd =3D create_loader_elf(insns, sizeof(insns), + map_data, sizeof(map_data), + license, sizeof(license), + false, true, false); + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "missing map sec sys_bpf return"); + ASSERT_EQ(errno, EINVAL, "missing map sec errno"); + + close(elf_fd); +} + +static void test_loader_load_fd_missing_license_sec(void) +{ + struct bpf_insn insns[] =3D { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }; + char map_data[] =3D "data"; + char license[] =3D "GPL"; + struct bpf_loader_ctx ctx =3D {}; + int elf_fd, err; + + elf_fd =3D create_loader_elf(insns, sizeof(insns), + map_data, sizeof(map_data), + license, sizeof(license), + false, false, true); + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "missing license sec sys_bpf return"); + ASSERT_EQ(errno, EINVAL, "missing license sec errno"); + + close(elf_fd); +} + +static void test_loader_load_fd_loader_failure(void) +{ + struct bpf_insn insns[] =3D { + BPF_MOV64_IMM(BPF_REG_0, -EPERM), + BPF_EXIT_INSN(), + }; + char map_data[] =3D "data"; + char license[] =3D "GPL"; + struct bpf_loader_ctx ctx =3D {}; + int elf_fd, err; + + elf_fd =3D create_loader_elf(insns, sizeof(insns), + map_data, sizeof(map_data), + license, sizeof(license), + false, false, false); + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &ctx, sizeof(ctx)); + ASSERT_EQ(err, -1, "loader failure sys_bpf return"); + ASSERT_EQ(errno, EPERM, "loader failure errno"); + + close(elf_fd); +} + +struct test_loader_lskel { + struct bpf_loader_ctx ctx; + struct { + struct bpf_map_desc test_map; + } maps; + struct { + struct bpf_prog_desc probe; + } progs; + struct { + int probe_fd; + } links; +}; + +static void test_loader_load_fd_lskel(void) +{ + struct test_loader_lskel skel =3D {}; + int elf_fd, err; + + skel.ctx.sz =3D (char *)&skel.links - (char *)&skel; + + /* Build fake ELF using extracted opts_insn (__loader.prog) and opts_data= (__loader.map) */ + elf_fd =3D create_loader_elf(test_loader_opts_insn, test_loader_opts_insn= _sz, + test_loader_opts_data, test_loader_opts_data_sz, + "GPL", sizeof("GPL"), + false, false, false); + if (!ASSERT_GE(elf_fd, 0, "create_loader_elf_lskel")) + return; + + err =3D sys_bpf_loader_load_fd(elf_fd, &skel.ctx, skel.ctx.sz); + ASSERT_OK(err, "sys_bpf_loader_load_fd lskel"); + + ASSERT_GT(skel.progs.probe.prog_fd, 0, "test_loader probe prog_fd > 0"); + ASSERT_GT(skel.maps.test_map.map_fd, 0, "test_loader test_map map_fd > 0"= ); + + if (skel.progs.probe.prog_fd > 0) + close(skel.progs.probe.prog_fd); + if (skel.maps.test_map.map_fd > 0) + close(skel.maps.test_map.map_fd); + close(elf_fd); +} + +void test_loader_load_fd(void) +{ + if (test__start_subtest("invalid_fd")) + test_loader_load_fd_invalid_fd(); + if (test__start_subtest("oversized_ctx")) + test_loader_load_fd_oversized_ctx(); + if (test__start_subtest("invalid_elf")) + test_loader_load_fd_invalid_elf(); + if (test__start_subtest("missing_prog_sec")) + test_loader_load_fd_missing_prog_sec(); + if (test__start_subtest("missing_map_sec")) + test_loader_load_fd_missing_map_sec(); + if (test__start_subtest("missing_license_sec")) + test_loader_load_fd_missing_license_sec(); + if (test__start_subtest("loader_failure")) + test_loader_load_fd_loader_failure(); + if (test__start_subtest("lskel")) + test_loader_load_fd_lskel(); +} diff --git a/tools/testing/selftests/bpf/progs/test_loader.c b/tools/testin= g/selftests/bpf/progs/test_loader.c new file mode 100644 index 000000000000..515e15ca042e --- /dev/null +++ b/tools/testing/selftests/bpf/progs/test_loader.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "vmlinux.h" +#include + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 4); + __type(key, __u32); + __type(value, __u64); +} test_map SEC(".maps"); + +SEC("socket") +int probe(void *ctx) +{ + __u32 key =3D 0; + __u64 *val =3D bpf_map_lookup_elem(&test_map, &key); + + return val ? (int)*val : 0; +} + +char _license[] SEC("license") =3D "GPL"; --=20 2.55.0.691.gc56d675ccc-goog