| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/28 18:30 | flow | patch-triage |
4mArgs:null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"KEYS",
"SYSTEM_DATA_VERIFICATION",
"ASYMMETRIC_KEY_TYPE",
"CRYPTO_MLDSA",
"CRYPTO_SHA512",
"CRYPTO_SHA256",
"CRYPTO_RSA"
],
"FocusSymbols": [
"bpf_lookup_keyring",
"bpf_keyring_enforced",
"key_register_bpf_keyring",
"lookup_user_key",
"bpf_check",
"bpf_verify_pkcs7_signature"
],
"KMSANReasoning": "The patch series adds support for the `.bpf` keyring (via the `bpf.keyring_unsealed` boot parameter, `KEY_SPEC_BPF_KEYRING` ID, and signature verifier integration) and tightens the maximum signature size check for BPF programs.\n\nAnalysis of uninitialized memory risk:\n1. No uninitialized data or struct padding is copied to userspace via `copy_to_user`, BPF maps, or syscall attributes.\n2. The newly introduced `struct bpf_key` allocation in `bpf_lookup_keyring()` explicitly initializes both of its struct fields (`key` and `has_ref`) prior to returning.\n3. No conditional branches rely on uninitialized stack or heap state.\n4. Signature verification operates on memory copied cleanly with `kvmemdup_bpfptr()`.\n\nAny potential bugs related to memory safety, pointer validation, or reference counting are standard lifetime or memory safety issues caught by KASAN/standard detectors. There are no KMSAN-specific uninitialized memory or info-leak risks that justify a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces a dedicated BPF keyring (.bpf) selectable via KEY_SPEC_BPF_KEYRING (-9), new kernel logic for lookup, restriction, and enforcement during BPF program verification, and support for ML-DSA signed program verification. These changes modify core BPF program loading and key management logic reachable from userspace.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/28 18:30 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit e339e4245c3ea6b0a065a66a530e37d1313633f6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Aug 28 18:30:36 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt\nindex e4643634a9b1b..e70fb15757d40 100644\n--- a/Documentation/admin-guide/kernel-parameters.txt\n+++ b/Documentation/admin-guide/kernel-parameters.txt\n@@ -667,6 +667,22 @@ Kernel parameters\n \n \t\t\tSee Documentation/admin-guide/bootconfig.rst\n \n+\tbpf.keyring_unsealed=\n+\t\t\t[BPF] When set to 1, leave the bpf keyring unsealed\n+\t\t\tafter initialization so that userspace can provision\n+\t\t\tkeys. Once the keyring is restricted it becomes active\n+\t\t\tand can be used for BPF program signature verification.\n+\n+\t\t\tSetting this also means that the bpf keyring is the\n+\t\t\tonly non-system keyring a loader may select for the\n+\t\t\trest of the boot: caller-supplied user/session\n+\t\t\tkeyrings are refused with -EPERM, whether or not\n+\t\t\tprovisioning actually completed. The system keyrings\n+\t\t\tstay selectable. Leaving it unset keeps the prior\n+\t\t\tbehaviour, where a caller-supplied keyring is allowed.\n+\n+\t\t\tSee Documentation/bpf/signing.rst\n+\n \tbttv.card=\t[HW,V4L] bttv (bt848 + bt878 based grabber cards)\n \tbttv.radio=\tMost important insmod options are available as\n \t\t\tkernel args too.\ndiff --git a/Documentation/bpf/signing.rst b/Documentation/bpf/signing.rst\nindex e73eaaebd8b15..27fdad3df96f1 100644\n--- a/Documentation/bpf/signing.rst\n+++ b/Documentation/bpf/signing.rst\n@@ -254,21 +254,24 @@ returned. Only after the program has fully loaded, at the next hook\n (``security_bpf_prog()``), does ``BPF_SIG_VERIFIED`` carry its full meaning:\n validly signed *and* fully verified.\n \n-A more realistic admission policy than \"is it signed at all\": accept programs\n-signed by a system keyring, accept a user-keyring signature only if the\n-key/keyring it was verified against is on an explicit allowlist, and emit a\n-tamper-evident record of every decision so that even denied attempts are\n-auditable. (Illustrative - error checking elided.)\n+A more realistic admission policy than \"is it signed at all\": base trust in\n+the bpf keyring or an explicit allowlist of a caller-supplied staging key/\n+keyring, and emit a record of every decision including denied attempts.\n+(Illustrative - error checking elided.)\n \n .. code-block:: c\n \n- /* Serials of user keys/keyrings we additionally trust. */\n+ /*\n+ * Serials of caller-supplied keyrings we are willing to stage. Empty\n+ * on a system that has committed to the bpf keyring, where the kernel\n+ * refuses them anyway.\n+ */\n struct {\n __uint(type, BPF_MAP_TYPE_HASH);\n __type(key, __s32); /* keyring_serial */\n __type(value, __u8);\n __uint(max_entries, 64);\n- } trusted_user_keys SEC(\".maps\");\n+ } staging_keys SEC(\".maps\");\n \n /* Audit stream consumed by a userspace logger. */\n struct {\n@@ -291,11 +294,18 @@ auditable. (Illustrative - error checking elided.)\n if (kernel)\n return 0; /* trust in-kernel loads */\n \n- if (verdict != BPF_SIG_VERIFIED)\n+ if (verdict != BPF_SIG_VERIFIED) {\n ret = -EPERM; /* must be validly signed */\n- else if (ktype == BPF_SIG_KEYRING_USER \u0026\u0026\n- !bpf_map_lookup_elem(\u0026trusted_user_keys, \u0026serial))\n- ret = -EPERM; /* key/keyring not allowlisted */\n+ } else switch (ktype) {\n+ case BPF_SIG_KEYRING_BPF:\n+ break;\n+ case BPF_SIG_KEYRING_USER:\n+ if (!bpf_map_lookup_elem(\u0026staging_keys, \u0026serial))\n+ ret = -EPERM;\n+ break;\n+ default:\n+ ret = -EPERM; /* keyring not in policy */\n+ }\n \n d = bpf_ringbuf_reserve(\u0026audit, sizeof(*d), 0);\n if (d) {\n@@ -309,6 +319,11 @@ auditable. (Illustrative - error checking elided.)\n return ret;\n }\n \n+Such a policy is what makes a caller-supplied keyring usable at all on a system\n+that does not boot with ``bpf.keyring_unsealed=1``: the allowlist bounds which\n+staged keys count, and the LSM itself has to protect them from being tampered\n+with.\n+\n Observing a verified load: ``security_bpf_prog()``\n --------------------------------------------------\n \n@@ -381,8 +396,9 @@ that verdict covered all of its exclusive maps, rejecting any that did not - so\n a deny-by-default admission policy needs no second enforcement point. Use\n ``security_bpf_prog()`` to record or finally gate the verified programs once\n they carry an id. The ``verdict``, ``keyring_type`` and ``keyring_serial`` fields\n-let a policy distinguish, for example, \"verified and signed by a builtin key\"\n-from \"verified by a user key\". A policy LSM such as IPE could consume the same\n+let a policy distinguish \"verified against the operator's bpf keyring\" from\n+\"verified against a keyring the loader supplied itself\", which is the\n+distinction that matters most. A policy LSM such as IPE could consume the same\n hooks to enforce system policy without writing any BPF, though none implements\n this today.\n \n@@ -390,33 +406,158 @@ Keyrings\n ========\n \n ``keyring_id`` selects the trusted keyring the PKCS#7 signature is verified\n-against. The well-known ids ``0`` (builtin), ``VERIFY_USE_SECONDARY_KEYRING``\n-and ``VERIFY_USE_PLATFORM_KEYRING`` select the corresponding system keyrings;\n-any other value is treated as the serial of a user/session key or keyring.\n-The keyring is looked up first, before the signature bytes are examined, so a\n-signature naming a non-existent keyring is rejected up front, and a failed\n-verification aborts the load - so a program that loads successfully with a\n-signature always has consistent keyring fields recorded.\n+against. Four values are well-known; anything else is resolved as a caller-\n+supplied key or keyring, named either by its serial or by one of the other\n+``KEY_SPEC_*`` ids:\n+\n+.. list-table::\n+ :header-rows: 1\n+\n+ * - ``keyring_id``\n+ - Keyring\n+ * - ``0``\n+ - builtin trusted keyring\n+ * - ``VERIFY_USE_SECONDARY_KEYRING`` (``1``)\n+ - secondary trusted keyring\n+ * - ``VERIFY_USE_PLATFORM_KEYRING`` (``2``)\n+ - platform keyring\n+ * - ``KEY_SPEC_BPF_KEYRING`` (``-9``)\n+ - the bpf keyring\n+ * - anything else\n+ - a caller-supplied user/session key or keyring\n+\n+The keyring is resolved first, before the signature bytes are examined, so a\n+signature naming a keyring that cannot be used is rejected up front, and a\n+failed verification aborts the load - a program that loads successfully with\n+a signature therefore always has consistent keyring fields recorded.\n+\n+The bpf keyring\n+---------------\n+\n+A system keyring needs a kernel rebuild or a vouched-for enrollment to rotate a\n+key, and grants BPF-signing trust to keys trusted for everything else in the\n+kernel too. A caller-supplied keyring, at the other extreme, is filled by the\n+very process that loads the program and so carries no trust of its own.\n+\n+The bpf keyring fills that gap and is the trust anchor which a signed BPF\n+deployment should be built on top of: a keyring named ``.bpf``, selected with\n+``KEY_SPEC_BPF_KEYRING``, that an operator provisions at boot with a key\n+scoped to BPF program loading and nothing else in the kernel's trust hierarchy.\n+It is owned by the operator rather than by the loader, and rotatable across a\n+reboot without touching the kernel image. It is modelled after the dm-verity\n+keyring (see ``dm_verity.keyring_unsealed=``) and provisioned the same way: an\n+initrd runs the ``keyctl`` steps below before handing off to the rootfs.\n+\n+Provisioning\n+~~~~~~~~~~~~\n+\n+The keyring is created during ``late_initcall`` and is **sealed empty** by\n+default: it carries a reject-all restriction, so no key can ever be added and\n+``KEY_SPEC_BPF_KEYRING`` fails with ``-ENOKEY`` for the whole boot.\n+\n+``bpf.keyring_unsealed=1`` leaves it unrestricted at init so the initrd can\n+provision it. The keyring is not linked into any process keyring, but the\n+``KEY_SPEC_BPF_KEYRING`` special key id addresses it directly, so its serial\n+does not have to be looked up first. Steps would be as follows::\n+\n+ keyctl padd asymmetric \"\" -9 \u003c signing_key.der\n+ keyctl restrict_keyring -9\n+\n+Both steps are required: the keyring is consulted only once it is **non-empty\n+and restricted**. An unrestricted keyring is ignored even when it holds keys,\n+so a half-provisioned keyring is inert rather than a weaker trust anchor, and a\n+load naming it fails with ``-ENOKEY`` and a verifier log. Restricting cannot\n+be undone.\n+\n+More than one key is enrolled by repeating the ``keyctl padd`` step; the\n+restriction is applied once, after the last of them::\n+\n+ for key in /etc/bpf/keys/*.der; do\n+ keyctl padd asymmetric \"\" -9 \u003c $key\n+ done\n+\n+ keyctl restrict_keyring -9\n+ keyctl show -9\n+\n+The restriction bounds what can be added, never what can be taken away. A key\n+that is already enrolled can still be unlinked, and the keyring cleared or\n+revoked, by anything running as root. That does not weaken the anchor, since\n+a keyring left empty is no longer consulted and a load naming it fails with\n+``-ENOKEY``, but it does take signed loading out until the next boot. Dropping\n+the user permissions the keyring no longer needs would close that; as a third\n+step in the initrd::\n+\n+ keyctl setperm -9 0x08030000\n+\n+What remains is ``KEY_POS_SEARCH`` for the in-kernel search during verification,\n+plus ``KEY_USR_VIEW`` and ``KEY_USR_READ`` so the keyring stays visible in\n+``/proc/keys``. ``KEY_SPEC_BPF_KEYRING`` names the keyring but conveys no rights\n+of its own - the keyring is not possessed by anyone, so the ``KEY_POS_*`` bits\n+are only ever reached by the in-kernel search, and userspace is left with what\n+the ``KEY_USR_*`` bits grant. Once they are dropped, addressing it by the serial\n+``/proc/keys`` reports gets no further than the special id does.\n+\n+Provisioning has to complete before control passes to the rootfs. The keyring\n+takes any number of keys for as long as it is unrestricted, so it is the\n+restriction that bounds the enrolled set, not the first enrollment. Before\n+the initrd hands off control, it must therefore restrict the keyring after\n+the last enrollment.\n+\n+Enforcement\n+~~~~~~~~~~~\n+\n+``bpf.keyring_unsealed=1`` states that the bpf keyring is *the* trust anchor for\n+this boot, so it does more than unseal. From the first program load onwards a\n+caller-supplied user/session keyring is refused with ``-EPERM`` and a verifier\n+log message, whether or not provisioning ever completed. The system keyrings\n+stay selectable.\n+\n+Enforcement is readable at ``/sys/module/bpf/parameters/keyring_unsealed``, and\n+read-only there: the flag is ``__ro_after_init`` behind a 0444 parameter. It is\n+also derived from the boot flag rather than from the keyring's runtime state,\n+so there is no window early in boot during which a caller-supplied keyring is\n+still accepted.\n+\n+Caller-supplied keyrings are for staging\n+----------------------------------------\n+\n+A ``keyring_id`` naming a user or session key or keyring is a *staging*\n+mechanism, not a trust anchor: it is filled by the same userspace that loads the\n+program, so verifying against it establishes only that the loader signed what it\n+loaded. Its purpose is to let software installed onto a running system - whose\n+signing key is not enrolled anywhere yet - run signed until that key reaches the\n+bpf keyring on the next boot.\n+\n+A system that has committed to the bpf keyring refuses this path outright (see\n+`Enforcement`_). A system that has not can still allow it, but a policy must\n+never treat ``BPF_SIG_KEYRING_USER`` as equivalent to the bpf or system\n+keyrings; it should allowlist the specific serials it is willing to stage and\n+pair that with a BPF LSM policy protecting those keys from tampering, as in\n+`Enforcement via LSMs`_.\n+\n+Recorded fields\n+---------------\n \n Two fields are recorded in ``prog-\u003eaux-\u003esig`` for an LSM to inspect:\n \n ``keyring_type`` (``enum bpf_sig_keyring``)\n Classified purely from ``keyring_id`` whenever the program is signed:\n ``BPF_SIG_KEYRING_BUILTIN``, ``_SECONDARY``, ``_PLATFORM`` for the system\n- keyrings, or ``_USER`` for a user/session keyring. It is\n- ``BPF_SIG_KEYRING_NONE`` for an unsigned program.\n+ keyrings, ``_BPF`` for the bpf keyring, or ``_USER`` for a caller-supplied\n+ user/session keyring. It is ``BPF_SIG_KEYRING_NONE`` for an unsigned\n+ program.\n \n ``keyring_serial`` (``s32``)\n Set **only** on a successful verification, to the serial of the\n- **user/session key or keyring** that ``keyring_id`` resolved to - the\n+ **caller-supplied key or keyring** that ``keyring_id`` resolved to - the\n object the signature was verified against, not the individual asymmetric\n key inside it that matched the signer. Passing\n ``KEY_SPEC_SESSION_KEYRING``, for example, records the session keyring's\n- serial. The system keyrings are trusted as a whole and expose no serial\n- here, so the serial is ``0`` for builtin, secondary and platform\n- signatures, and ``0`` for unsigned programs. In other words, a non-zero\n- ``keyring_serial`` is exactly \"verified against the user key/keyring with\n- this serial\".\n+ serial. The system keyrings and the bpf keyring are trusted as a whole and\n+ expose no serial here, so the serial is ``0`` for them, and ``0`` for\n+ unsigned programs. A non-zero ``keyring_serial`` is therefore exactly\n+ \"verified against the caller-supplied key/keyring with this serial\", which\n+ is exactly the case a policy has to scrutinise.\n \n .. list-table::\n :header-rows: 1\n@@ -436,16 +577,51 @@ Two fields are recorded in ``prog-\u003eaux-\u003esig`` for an LSM to inspect:\n * - ``VERIFY_USE_PLATFORM_KEYRING``\n - ``BPF_SIG_KEYRING_PLATFORM``\n - ``0``\n- * - other (a user/session key serial)\n+ * - ``KEY_SPEC_BPF_KEYRING``\n+ - ``BPF_SIG_KEYRING_BPF``\n+ - ``0``\n+ * - other (a caller-supplied key serial)\n - ``BPF_SIG_KEYRING_USER``\n - serial of the resolved key/keyring\n \n-Producing a signed object\n-==========================\n+Producing and loading a signed object\n+=====================================\n+\n+Generating a signing key\n+------------------------\n+\n+Signing is algorithm agnostic: the algorithm comes from the X.509 certificate\n+and the PKCS#7 ``SignerInfo``. Anything the X.509 and PKCS#7 parsers understand\n+works with no BPF-side change. Both examples below read the certificate request\n+config from ``x509.genkey``, for which ``certs/x509.genkey`` serves as a\n+template (see Documentation/admin-guide/module-signing.rst). RSA::\n+\n+ openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \\\n+ -config x509.genkey -outform PEM \\\n+ -out signing_key.pem -keyout signing_key.pem\n+ openssl x509 -in signing_key.pem -outform der -out signing_key.der\n+\n+ML-DSA-87 (FIPS-204), which needs openssl 3.5 or later, and both\n+``CONFIG_CRYPTO_MLDSA`` and ``CONFIG_CRYPTO_SHA512`` in the kernel - the latter\n+for the signedAttrs digest described below, which ``CONFIG_CRYPTO_MLDSA`` does\n+not select. Note the absence of a digest option: ML-DSA hashes the message\n+itself, so openssl ignores an explicit digest for it::\n+\n+ openssl req -new -nodes -utf8 -days 36500 -batch -x509 \\\n+ -newkey ML-DSA-87 -config x509.genkey -outform PEM \\\n+ -out signing_key.pem -keyout signing_key.pem\n+ openssl x509 -in signing_key.pem -outform der -out signing_key.der\n+\n+``bpftool`` handles the following internally: openssl before 4.0 cannot combine\n+ML-DSA with ``CMS_NOATTR``, so it falls back to signedAttrs, where only SHA-512\n+is permitted. This mirrors what module signing does as well.\n+\n+Signing\n+-------\n \n ``bpftool`` generates and signs a light skeleton in one step::\n \n- bpftool gen skeleton -L -S -k \u003cprivate_key.pem\u003e -i \u003ccertificate.x509\u003e \\\n+ bpftool gen skeleton -L -S -k signing_key.pem -i signing_key.der \\\n obj.bpf.o \u003e obj.lskel.h\n \n ``-L`` selects the light-skeleton (``gen_loader``) backend and ``-S`` enables\n@@ -454,12 +630,36 @@ signing; ``-k`` and ``-i`` supply the signing key and its X.509 certificate.\n reconstructs - and also computes ``excl_prog_hash`` as the digest of the loader\n instructions so the metadata map can be bound to the loader. The signature and\n hash are embedded in the generated header; the certificate is used only for\n-signing and is not included. Loading the skeleton performs the\n-create/populate/freeze/load sequence described above.\n+signing and is not included.\n+\n+Loading\n+-------\n+\n+The generated skeleton exposes ``keyring_id``, which selects the keyring the\n+kernel verifies against. Set it between open and load; loading then performs\n+the create/populate/freeze/load sequence described above::\n \n-At runtime the trusted public key must be present in the chosen keyring (for\n-example added to the session keyring, or built into the kernel's builtin trusted\n-keyring) for verification to succeed.\n+ struct obj *skel = obj__open();\n+\n+ skel-\u003ekeyring_id = KEY_SPEC_BPF_KEYRING;\n+ err = obj__load(skel);\n+\n+For the staging case the same object is loaded against a keyring the caller\n+populated itself, which only works on a system that has not set\n+``bpf.keyring_unsealed=1``::\n+\n+ /*\n+ * Staging only: this keyring is under the loader's own control and\n+ * carries no trust of its own. See \"Caller-supplied keyrings are for\n+ * staging\".\n+ */\n+ key_id = add_key(\"asymmetric\", \"\", der, der_sz, KEY_SPEC_SESSION_KEYRING);\n+ skel-\u003ekeyring_id = KEY_SPEC_SESSION_KEYRING;\n+ err = obj__load(skel);\n+\n+Either way the trusted public key must already be in the chosen keyring for\n+verification to succeed. For the bpf keyring that enrollment happens once at\n+boot, see `Provisioning`_.\n \n UAPI reference\n ==============\n@@ -487,6 +687,13 @@ UAPI reference\n The map content is not hashed separately at all - it is covered, as bytes,\n by the program signature.\n \n+Kernel command line:\n+\n+``bpf.keyring_unsealed=``\n+ Set to ``1`` to leave the bpf keyring unsealed for provisioning, and to make\n+ it the only non-system keyring a loader may select for the rest of the boot\n+ (see `The bpf keyring`_).\n+\n Notes and limitations\n ======================\n \n@@ -495,3 +702,5 @@ Notes and limitations\n exceed it.\n - The metadata container is a single-element array map, accessed through\n ``map_direct_value_addr``.\n+- The bpf keyring needs ``CONFIG_KEYS``; without it there is no bpf keyring\n+ and ``KEY_SPEC_BPF_KEYRING`` never resolves.\ndiff --git a/include/linux/bpf.h b/include/linux/bpf.h\nindex c2027688be3e4..3a7eb2185c354 100644\n--- a/include/linux/bpf.h\n+++ b/include/linux/bpf.h\n@@ -1735,6 +1735,7 @@ enum bpf_sig_keyring {\n \tBPF_SIG_KEYRING_SECONDARY,\n \tBPF_SIG_KEYRING_PLATFORM,\n \tBPF_SIG_KEYRING_USER,\n+\tBPF_SIG_KEYRING_BPF,\n };\n \n struct bpf_prog_aux {\n@@ -3819,6 +3820,8 @@ struct bpf_key {\n #if defined(CONFIG_KEYS) \u0026\u0026 defined(CONFIG_BPF_SYSCALL)\n struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);\n struct bpf_key *bpf_lookup_system_key(u64 id);\n+struct bpf_key *bpf_lookup_keyring(void);\n+bool bpf_keyring_enforced(void);\n void bpf_key_put(struct bpf_key *bkey);\n int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\n \t\t\t const struct bpf_dynptr *sig_p,\n@@ -3839,6 +3842,16 @@ static inline struct bpf_key *bpf_lookup_system_key(u64 id)\n \treturn NULL;\n }\n \n+static inline struct bpf_key *bpf_lookup_keyring(void)\n+{\n+\treturn NULL;\n+}\n+\n+static inline bool bpf_keyring_enforced(void)\n+{\n+\treturn false;\n+}\n+\n static inline void bpf_key_put(struct bpf_key *bkey)\n {\n }\ndiff --git a/include/linux/key.h b/include/linux/key.h\nindex 81b8f05c68985..bd10fe45819db 100644\n--- a/include/linux/key.h\n+++ b/include/linux/key.h\n@@ -440,6 +440,8 @@ extern key_ref_t keyring_search(key_ref_t keyring,\n extern int keyring_restrict(key_ref_t keyring, const char *type,\n \t\t\t const char *restriction);\n \n+extern void key_register_bpf_keyring(struct key *keyring);\n+\n extern struct key *key_lookup(key_serial_t id);\n \n static inline key_serial_t key_serial(const struct key *key)\ndiff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h\nindex 4c8884eea8084..fa85b9760391a 100644\n--- a/include/uapi/linux/keyctl.h\n+++ b/include/uapi/linux/keyctl.h\n@@ -24,6 +24,7 @@\n #define KEY_SPEC_GROUP_KEYRING\t\t-6\t/* - key ID for GID-specific keyring */\n #define KEY_SPEC_REQKEY_AUTH_KEY\t-7\t/* - key ID for assumed request_key auth key */\n #define KEY_SPEC_REQUESTOR_KEYRING\t-8\t/* - key ID for request_key() dest keyring */\n+#define KEY_SPEC_BPF_KEYRING\t\t-9\t/* - key ID for the BPF-specific keyring */\n \n /* request-key default keyrings */\n #define KEY_REQKEY_DEFL_NO_CHANGE\t\t-1\ndiff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile\nindex 90255d80e5be6..9a92c348bbda6 100644\n--- a/kernel/bpf/Makefile\n+++ b/kernel/bpf/Makefile\n@@ -27,6 +27,9 @@ obj-$(CONFIG_BPF_SYSCALL) += offload.o\n obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o\n obj-$(CONFIG_BPF_SYSCALL) += tcx.o\n endif\n+ifeq ($(CONFIG_KEYS),y)\n+obj-$(CONFIG_BPF_SYSCALL) += keys.o\n+endif\n ifeq ($(CONFIG_PERF_EVENTS),y)\n obj-$(CONFIG_BPF_SYSCALL) += stackmap.o\n endif\ndiff --git a/kernel/bpf/keys.c b/kernel/bpf/keys.c\nnew file mode 100644\nindex 0000000000000..bffc356839ac5\n--- /dev/null\n+++ b/kernel/bpf/keys.c\n@@ -0,0 +1,73 @@\n+// SPDX-License-Identifier: GPL-2.0-only\n+/* Copyright (c) 2026 Isovalent */\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003clinux/cred.h\u003e\n+#include \u003clinux/err.h\u003e\n+#include \u003clinux/init.h\u003e\n+#include \u003clinux/key.h\u003e\n+#include \u003clinux/moduleparam.h\u003e\n+#include \u003clinux/slab.h\u003e\n+\n+#undef MODULE_PARAM_PREFIX\n+#define MODULE_PARAM_PREFIX \"bpf.\"\n+\n+static struct key *bpf_keyring;\n+\n+static bool bpf_keyring_unsealed __ro_after_init;\n+module_param_named(keyring_unsealed, bpf_keyring_unsealed, bool, 0444);\n+MODULE_PARM_DESC(keyring_unsealed, \"Leave the bpf keyring unsealed\");\n+\n+bool bpf_keyring_enforced(void)\n+{\n+\treturn bpf_keyring_unsealed;\n+}\n+\n+struct bpf_key *bpf_lookup_keyring(void)\n+{\n+\tstruct bpf_key *bkey;\n+\n+\tif (!bpf_keyring)\n+\t\treturn NULL;\n+\tif (!READ_ONCE(bpf_keyring-\u003ekeys.nr_leaves_on_tree) ||\n+\t !READ_ONCE(bpf_keyring-\u003erestrict_link))\n+\t\treturn NULL;\n+\n+\tbkey = kmalloc_obj(*bkey);\n+\tif (!bkey)\n+\t\treturn NULL;\n+\n+\tbkey-\u003ekey = bpf_keyring;\n+\tbkey-\u003ehas_ref = false;\n+\treturn bkey;\n+}\n+\n+static int __init bpf_keyring_init(void)\n+{\n+\tstruct key *keyring;\n+\n+\tkeyring = keyring_alloc(\".bpf\",\n+\t\t\t\tGLOBAL_ROOT_UID, GLOBAL_ROOT_GID,\n+\t\t\t\tcurrent_cred(), KEY_POS_SEARCH |\n+\t\t\t\tKEY_USR_VIEW | KEY_USR_READ |\n+\t\t\t\tKEY_USR_WRITE | KEY_USR_SEARCH |\n+\t\t\t\tKEY_USR_SETATTR, KEY_ALLOC_NOT_IN_QUOTA,\n+\t\t\t\tNULL, NULL);\n+\tif (IS_ERR(keyring)) {\n+\t\tpr_err(\"bpf: cannot allocate bpf keyring: %ld\\n\",\n+\t\t PTR_ERR(keyring));\n+\t\treturn 0;\n+\t}\n+\tif (!bpf_keyring_unsealed \u0026\u0026\n+\t keyring_restrict(make_key_ref(keyring, true), NULL, NULL)) {\n+\t\tpr_err(\"bpf: cannot seal bpf keyring\\n\");\n+\t\tkey_revoke(keyring);\n+\t\tkey_put(keyring);\n+\t\treturn 0;\n+\t}\n+\n+\tbpf_keyring = keyring;\n+\tkey_register_bpf_keyring(keyring);\n+\treturn 0;\n+}\n+late_initcall(bpf_keyring_init);\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex e036ae20bf6b9..d50a135466bb6 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -24,6 +24,7 @@\n #include \u003clinux/bpf_lsm.h\u003e\n #include \u003clinux/security.h\u003e\n #include \u003clinux/verification.h\u003e\n+#include \u003clinux/keyctl.h\u003e\n #include \u003clinux/btf_ids.h\u003e\n #include \u003clinux/poison.h\u003e\n #include \u003clinux/module.h\u003e\n@@ -20972,6 +20973,14 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \treturn 0;\n }\n \n+/*\n+ * Upper bound on the PKCS#7 signature blob passed with a program. Comfortably\n+ * above the largest signature the kernel can verify, and far below anything\n+ * that would make rejecting a load expensive. Deliberately a fixed number so\n+ * that what the syscall accepts does not depend on PAGE_SIZE.\n+ */\n+#define BPF_PROG_MAX_SIGNATURE_SIZE\t(64 * 1024)\n+\n static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)\n {\n \tswitch (keyring_id) {\n@@ -20981,6 +20990,8 @@ static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)\n \t\treturn BPF_SIG_KEYRING_SECONDARY;\n \tcase (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:\n \t\treturn BPF_SIG_KEYRING_PLATFORM;\n+\tcase KEY_SPEC_BPF_KEYRING:\n+\t\treturn BPF_SIG_KEYRING_BPF;\n \tdefault:\n \t\treturn BPF_SIG_KEYRING_USER;\n \t}\n@@ -21009,18 +21020,25 @@ static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n \tu64 data_sz;\n \tint err = 0;\n \n-\t/*\n-\t * Don't attempt to use kmalloc_large or vmalloc for signatures.\n-\t * Practical signature for BPF program should be below this limit.\n-\t */\n \tif (!attr-\u003esignature_size ||\n-\t attr-\u003esignature_size \u003e KMALLOC_MAX_CACHE_SIZE)\n+\t attr-\u003esignature_size \u003e BPF_PROG_MAX_SIGNATURE_SIZE)\n \t\treturn -EINVAL;\n-\tif (system_keyring_id_check(attr-\u003ekeyring_id) == 0)\n+\n+\tif (system_keyring_id_check(attr-\u003ekeyring_id) == 0) {\n \t\tkey = bpf_lookup_system_key(attr-\u003ekeyring_id);\n-\telse\n+\t} else if (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n+\t\tkey = bpf_lookup_keyring();\n+\t} else if (bpf_keyring_enforced()) {\n+\t\tverbose(env, \"caller-supplied keyring refused, use bpf keyring\\n\");\n+\t\treturn -EPERM;\n+\t} else {\n \t\tkey = bpf_lookup_user_key(attr-\u003ekeyring_id, 0);\n+\t}\n \tif (!key) {\n+\t\tif (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n+\t\t\tverbose(env, \"the bpf keyring is empty or has not been restricted\\n\");\n+\t\t\treturn -ENOKEY;\n+\t\t}\n \t\tverbose(env, \"cannot resolve signing keyring with keyring_id %d\\n\",\n \t\t\tattr-\u003ekeyring_id);\n \t\treturn -EINVAL;\ndiff --git a/security/keys/process_keys.c b/security/keys/process_keys.c\nindex a63c46bb2d148..44358388e3959 100644\n--- a/security/keys/process_keys.c\n+++ b/security/keys/process_keys.c\n@@ -22,6 +22,9 @@\n /* Session keyring create vs join semaphore */\n static DEFINE_MUTEX(key_session_mutex);\n \n+/* BPF keyring reachable through KEY_SPEC_BPF_KEYRING */\n+static struct key *bpf_keyring __ro_after_init;\n+\n /* The root user's tracking struct */\n struct key_user root_key_user = {\n \t.usage\t\t= REFCOUNT_INIT(3),\n@@ -590,6 +593,20 @@ bool lookup_user_key_possessed(const struct key *key,\n \treturn key == match_data-\u003eraw_data;\n }\n \n+/**\n+ * key_register_bpf_keyring - Publish the BPF keyring for KEY_SPEC_BPF_KEYRING\n+ * @keyring: The keyring to publish\n+ *\n+ * Make @keyring reachable by userspace through the KEY_SPEC_BPF_KEYRING\n+ * special key ID, so that provisioning it does not require scraping its\n+ * serial out of /proc/keys first. Called once, from an initcall, and never\n+ * undone.\n+ */\n+void key_register_bpf_keyring(struct key *keyring)\n+{\n+\tbpf_keyring = keyring;\n+}\n+\n /*\n * Look up a key ID given us by userspace with a given permissions mask to get\n * the key it refers to.\n@@ -741,6 +758,14 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,\n \t\tkey_ref = make_key_ref(key, 1);\n \t\tbreak;\n \n+\tcase KEY_SPEC_BPF_KEYRING:\n+\t\tkey = bpf_keyring;\n+\t\tif (!key)\n+\t\t\tgoto error;\n+\t\t__key_get(key);\n+\t\tkey_ref = make_key_ref(key, 0);\n+\t\tbreak;\n+\n \tdefault:\n \t\tkey_ref = ERR_PTR(-EINVAL);\n \t\tif (id \u003c 1)\ndiff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h\nindex 78b6e0ebb85d8..9315a1db1f7c2 100644\n--- a/tools/bpf/bpftool/main.h\n+++ b/tools/bpf/bpftool/main.h\n@@ -57,7 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)\n \t})\n \n #define ERR_MAX_LEN\t1024\n-#define MAX_SIG_SIZE\t4096\n+#define MAX_SIG_SIZE\t16384\n \n #define BPF_TAG_FMT\t\"%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\"\n \ndiff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c\nindex 88726a6db6d0e..adbfd4ae5c021 100644\n--- a/tools/bpf/bpftool/sign.c\n+++ b/tools/bpf/bpftool/sign.c\n@@ -130,6 +130,12 @@ __u32 register_session_key(const char *key_der_path)\n \n int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)\n {\n+\tunsigned int signer_flags = CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |\n+#ifdef CMS_NO_SIGNING_TIME\n+\t\t\t\t CMS_NO_SIGNING_TIME |\n+#endif\n+\t\t\t\t CMS_USE_KEYID | CMS_NOATTR;\n+\tconst EVP_MD *cms_digest = EVP_sha256();\n \tBIO *bd_in = NULL, *bd_out = NULL;\n \tEVP_PKEY *private_key = NULL;\n \tCMS_ContentInfo *cms = NULL;\n@@ -167,6 +173,20 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)\n \t\tgoto cleanup;\n \t}\n \n+#if OPENSSL_VERSION_NUMBER \u003e= 0x30000000L \u0026\u0026 OPENSSL_VERSION_NUMBER \u003c 0x40000000L\n+\tif (EVP_PKEY_is_a(private_key, \"ML-DSA-44\") ||\n+\t EVP_PKEY_is_a(private_key, \"ML-DSA-65\") ||\n+\t EVP_PKEY_is_a(private_key, \"ML-DSA-87\")) {\n+\t\t/*\n+\t\t * See workaround in 0ad9a71933e7 (\"modsign: Enable ML-DSA\n+\t\t * module signing\"). Kernel only accepts sha512, see also\n+\t\t * 8bbdeb7a25b4 (\"pkcs7, x509: Add ML-DSA support\").\n+\t\t */\n+\t\tsigner_flags \u0026= ~CMS_NOATTR;\n+\t\tcms_digest = EVP_sha512();\n+\t}\n+#endif\n+\n \tcms = CMS_sign(NULL, NULL, NULL, NULL,\n \t\t CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |\n \t\t\t CMS_STREAM);\n@@ -175,9 +195,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)\n \t\tgoto cleanup;\n \t}\n \n-\tif (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),\n-\t\t\t CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |\n-\t\t\t CMS_USE_KEYID | CMS_NOATTR)) {\n+\tif (!CMS_add1_signer(cms, x509, private_key, cms_digest, signer_flags)) {\n \t\terr = -EINVAL;\n \t\tgoto cleanup;\n \t}\ndiff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile\nindex 5f1a3bfc0569f..05b3ee64290fa 100644\n--- a/tools/testing/selftests/bpf/Makefile\n+++ b/tools/testing/selftests/bpf/Makefile\n@@ -663,7 +663,7 @@ $(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)\n \t}) \u0026\u0026 \\\n \trm -f $$(\u003c:.o=.llinked1.o) $$(\u003c:.o=.llinked2.o) $$(\u003c:.o=.llinked3.o)\n \n-$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)\n+$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) $(VERIFICATION_CERT) | $(TRUNNER_OUTPUT)\n \t$(Q)$(if $(PERMISSIVE),if [ ! -f $$\u003c ]; then\t\t\t\\\n \t\t$$(RM) $$@;\t\t\t\t\t\t\\\n \t\tprintf ' %-12s %s\\n' 'SKIP-SKEL' '$$(notdir $$@)' 1\u003e\u00262; \\\ndiff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config\nindex ea7044f30adc3..2f79688dcf7ce 100644\n--- a/tools/testing/selftests/bpf/config\n+++ b/tools/testing/selftests/bpf/config\n@@ -12,7 +12,9 @@ CONFIG_BPF_SYSCALL=y\n # CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set\n CONFIG_CGROUP_BPF=y\n CONFIG_CRYPTO_HMAC=y\n+CONFIG_CRYPTO_MLDSA=y\n CONFIG_CRYPTO_SHA256=y\n+CONFIG_CRYPTO_SHA512=y\n CONFIG_CRYPTO_USER_API=y\n CONFIG_CRYPTO_USER_API_HASH=y\n CONFIG_CRYPTO_USER_API_SKCIPHER=y\ndiff --git a/tools/testing/selftests/bpf/prog_tests/signed_loader.c b/tools/testing/selftests/bpf/prog_tests/signed_loader.c\nindex 77381d345435c..a0f93756e717b 100644\n--- a/tools/testing/selftests/bpf/prog_tests/signed_loader.c\n+++ b/tools/testing/selftests/bpf/prog_tests/signed_loader.c\n@@ -32,11 +32,22 @@ enum {\n \tBPF_SIG_KEYRING_SECONDARY,\n \tBPF_SIG_KEYRING_PLATFORM,\n \tBPF_SIG_KEYRING_USER,\n+\tBPF_SIG_KEYRING_BPF,\n };\n \n-static int load_loader(const void *insns, __u32 insns_sz, int map_fd,\n-\t\t const void *sig, __u32 sig_sz, __s32 keyring_id,\n-\t\t __u32 fd_array_cnt)\n+#ifndef KEY_SPEC_BPF_KEYRING\n+#define KEY_SPEC_BPF_KEYRING\t-9\n+#endif\n+\n+/* verify_sig_setup.sh exits with this when openssl cannot do ML-DSA. */\n+#define SETUP_SKIP\t\t(-77)\n+\n+/* FIPS-204 ML-DSA-87 signature size, see include/crypto/mldsa.h. */\n+#define MLDSA87_SIGNATURE_SIZE\t4627\n+\n+static int load_loader_log(const void *insns, __u32 insns_sz, int map_fd,\n+\t\t\t const void *sig, __u32 sig_sz, __s32 keyring_id,\n+\t\t\t __u32 fd_array_cnt, char *log_buf, __u32 log_sz)\n {\n \tunion bpf_attr attr;\n \tint fd;\n@@ -48,18 +59,31 @@ static int load_loader(const void *insns, __u32 insns_sz, int map_fd,\n \tattr.license = ptr_to_u64(\"Dual BSD/GPL\");\n \tattr.prog_flags = BPF_F_SLEEPABLE;\n \tattr.fd_array = ptr_to_u64(\u0026map_fd);\n+\tattr.fd_array_cnt = fd_array_cnt;\n \tif (sig) {\n \t\tattr.signature = ptr_to_u64(sig);\n \t\tattr.signature_size = sig_sz;\n \t\tattr.keyring_id = keyring_id;\n \t}\n-\tattr.fd_array_cnt = fd_array_cnt;\n+\tif (log_buf) {\n+\t\tattr.log_level = 1;\n+\t\tattr.log_buf = ptr_to_u64(log_buf);\n+\t\tattr.log_size = log_sz;\n+\t}\n \tmemcpy(attr.prog_name, \"__loader.prog\", sizeof(\"__loader.prog\"));\n \tfd = syscall(__NR_bpf, BPF_PROG_LOAD, \u0026attr,\n \t\t offsetofend(union bpf_attr, keyring_id));\n \treturn fd \u003c 0 ? -errno : fd;\n }\n \n+static int load_loader(const void *insns, __u32 insns_sz, int map_fd,\n+\t\t const void *sig, __u32 sig_sz, __s32 keyring_id,\n+\t\t __u32 fd_array_cnt)\n+{\n+\treturn load_loader_log(insns, insns_sz, map_fd, sig, sig_sz, keyring_id,\n+\t\t\t fd_array_cnt, NULL, 0);\n+}\n+\n static int run_gen_loader(const void *insns, __u32 insns_sz,\n \t\t\t const void *data, __u32 data_sz,\n \t\t\t const void *excl, __u32 excl_sz,\n@@ -156,12 +180,30 @@ static int run_setup(const char *cmd, const char *dir)\n \t}\n \tif (waitpid(pid, \u0026status, 0) \u003c 0)\n \t\treturn -errno;\n-\treturn (WIFEXITED(status) \u0026\u0026\n-\t\tWEXITSTATUS(status) == 0) ? 0 : -EINVAL;\n+\tif (!WIFEXITED(status))\n+\t\treturn -EINVAL;\n+\treturn -WEXITSTATUS(status);\n }\n \n-static int sign_buf(const char *dir, const void *buf, __u32 len,\n-\t\t void *sig, __u32 *sig_sz)\n+static void genkey_dir_fini(const char *dir)\n+{\n+\tstatic const char * const files[] = {\n+\t\t\"signing_key.der\", \"signing_key.pem\", \"x509.genkey\",\n+\t};\n+\tchar path[PATH_MAX];\n+\tsize_t i;\n+\n+\tif (!dir)\n+\t\treturn;\n+\tfor (i = 0; i \u003c ARRAY_SIZE(files); i++) {\n+\t\tsnprintf(path, sizeof(path), \"%s/%s\", dir, files[i]);\n+\t\tunlink(path);\n+\t}\n+\trmdir(dir);\n+}\n+\n+static int sign_buf_digest(const char *dir, const void *buf, __u32 len,\n+\t\t\t void *sig, __u32 *sig_sz, const char *digest)\n {\n \tchar data_tmpl[PATH_MAX], key[PATH_MAX];\n \tchar sigpath[PATH_MAX + sizeof(\".p7s\")];\n@@ -176,6 +218,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,\n \tfd = mkstemp(data_tmpl);\n \tif (fd \u003c 0)\n \t\treturn -errno;\n+\tsnprintf(sigpath, sizeof(sigpath), \"%s.p7s\", data_tmpl);\n \tif (write(fd, buf, len) != (ssize_t)len) {\n \t\tclose(fd);\n \t\tret = -EIO;\n@@ -190,7 +233,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,\n \t}\n \tif (pid == 0) {\n \t\tsnprintf(key, sizeof(key), \"%s/signing_key.pem\", dir);\n-\t\texeclp(\"./sign-file\", \"./sign-file\", \"-d\", \"sha256\",\n+\t\texeclp(\"./sign-file\", \"./sign-file\", \"-d\", digest,\n \t\t key, key, data_tmpl, NULL);\n \t\texit(1);\n \t}\n@@ -200,34 +243,38 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,\n \t\tgoto out;\n \t}\n \n-\tsnprintf(sigpath, sizeof(sigpath), \"%s.p7s\", data_tmpl);\n \tif (stat(sigpath, \u0026st) \u003c 0) {\n \t\tret = -errno;\n \t\tgoto out;\n \t}\n \tif (st.st_size \u003e (off_t)*sig_sz) {\n \t\tret = -E2BIG;\n-\t\tgoto out_sig;\n+\t\tgoto out;\n \t}\n \tfd = open(sigpath, O_RDONLY);\n \tif (fd \u003c 0) {\n \t\tret = -errno;\n-\t\tgoto out_sig;\n+\t\tgoto out;\n \t}\n \tif (read(fd, sig, st.st_size) != st.st_size) {\n \t\tclose(fd);\n \t\tret = -EIO;\n-\t\tgoto out_sig;\n+\t\tgoto out;\n \t}\n \tclose(fd);\n \t*sig_sz = st.st_size;\n-out_sig:\n-\tunlink(sigpath);\n out:\n+\tunlink(sigpath);\n \tunlink(data_tmpl);\n \treturn ret;\n }\n \n+static int sign_buf(const char *dir, const void *buf, __u32 len,\n+\t\t void *sig, __u32 *sig_sz)\n+{\n+\treturn sign_buf_digest(dir, buf, len, sig, sig_sz, \"sha256\");\n+}\n+\n struct gen_loader_fixture {\n \tstruct test_signed_loader *skel;\n \tstruct gen_loader_opts gopts;\n@@ -457,7 +504,7 @@ static void signed_btf_fd_array_rejected(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -529,7 +576,6 @@ static void signature_failure_logs(void)\n \tstatic const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };\n \tchar log_buf[1024] = {};\n \tstruct gen_loader_fixture f;\n-\tunion bpf_attr attr;\n \tint fd;\n \n \tif (gen_loader_fixture_init(\u0026f) == 0) {\n@@ -538,22 +584,9 @@ static void signature_failure_logs(void)\n \t\t * failure is reported through the verifier log. A present-but-\n \t\t * invalid signature is rejected and the log says why.\n \t\t */\n-\t\tmemset(\u0026attr, 0, sizeof(attr));\n-\t\tattr.prog_type = BPF_PROG_TYPE_SYSCALL;\n-\t\tattr.insns = ptr_to_u64(f.gopts.insns);\n-\t\tattr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn);\n-\t\tattr.license = ptr_to_u64(\"Dual BSD/GPL\");\n-\t\tattr.prog_flags = BPF_F_SLEEPABLE;\n-\t\tattr.signature = ptr_to_u64(junk);\n-\t\tattr.signature_size = sizeof(junk);\n-\t\tattr.keyring_id = KEY_SPEC_SESSION_KEYRING;\n-\t\tattr.log_level = 1;\n-\t\tattr.log_buf = ptr_to_u64(log_buf);\n-\t\tattr.log_size = sizeof(log_buf);\n-\t\tmemcpy(attr.prog_name, \"__loader.prog\", sizeof(\"__loader.prog\"));\n-\n-\t\tfd = syscall(__NR_bpf, BPF_PROG_LOAD, \u0026attr,\n-\t\t\t offsetofend(union bpf_attr, keyring_id));\n+\t\tfd = load_loader_log(f.gopts.insns, f.gopts.insns_sz, -1, junk,\n+\t\t\t\t sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0,\n+\t\t\t\t log_buf, sizeof(log_buf));\n \t\tASSERT_LT(fd, 0, \"invalid signature rejected at load\");\n \t\tif (fd \u003e= 0)\n \t\t\tclose(fd);\n@@ -571,8 +604,9 @@ static void signature_too_large(void)\n \n \tif (gen_loader_fixture_init(\u0026f) == 0) {\n \t\t/*\n-\t\t * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE)\n-\t\t * is rejected before the buffer is read.\n+\t\t * signature_size beyond the kernel's bound\n+\t\t * (BPF_PROG_MAX_SIGNATURE_SIZE) is rejected before the buffer\n+\t\t * is read.\n \t\t */\n \t\tfd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,\n \t\t\t\t 64 \u003c\u003c 20, KEY_SPEC_SESSION_KEYRING, 0);\n@@ -626,6 +660,280 @@ static void signature_bad_keyring(void)\n \tgen_loader_fixture_fini(\u0026f);\n }\n \n+static bool keyring_unsealed_boot(void)\n+{\n+\tchar val = 0;\n+\tint fd;\n+\n+\tfd = open(\"/sys/module/bpf/parameters/keyring_unsealed\", O_RDONLY);\n+\tif (fd \u003c 0)\n+\t\treturn false;\n+\tif (read(fd, \u0026val, 1) != 1)\n+\t\tval = 0;\n+\tclose(fd);\n+\treturn val == 'Y' || val == '1';\n+}\n+\n+static int bpf_keyring_lookup(int *nr_keys)\n+{\n+\tchar line[512], type[32], desc[64];\n+\tint serial = -ENOENT;\n+\tFILE *f;\n+\n+\tf = fopen(\"/proc/keys\", \"r\");\n+\tif (!f)\n+\t\treturn -errno;\n+\n+\twhile (fgets(line, sizeof(line), f)) {\n+\t\tunsigned int hex;\n+\t\tchar *sum;\n+\n+\t\tif (sscanf(line, \"%x %*s %*s %*s %*s %*s %*s %31s %63s\",\n+\t\t\t \u0026hex, type, desc) != 3)\n+\t\t\tcontinue;\n+\t\tif (strcmp(type, \"keyring\") || strcmp(desc, \".bpf:\"))\n+\t\t\tcontinue;\n+\n+\t\tserial = (int)hex;\n+\t\tif (nr_keys) {\n+\t\t\tsum = strstr(line, \".bpf: \");\n+\t\t\t*nr_keys = !sum || !strncmp(sum + 6, \"empty\", 5) ?\n+\t\t\t\t 0 : atoi(sum + 6);\n+\t\t}\n+\t\tbreak;\n+\t}\n+\tfclose(f);\n+\treturn serial;\n+}\n+\n+static long keyctl_ret(int cmd, unsigned long arg2, unsigned long arg3)\n+{\n+\tlong ret = syscall(__NR_keyctl, cmd, arg2, arg3);\n+\n+\treturn ret \u003c 0 ? -errno : ret;\n+}\n+\n+/*\n+ * What the bpf keyring still needs once it got provisioned: KEY_POS_SEARCH\n+ * for the in-kernel search during verification, and the user view/read bits\n+ * so it stays visible in /proc/keys, rest is dropped so the enrolled is\n+ * therefore final.\n+ */\n+#define BPF_KEYRING_PERM_LOCKED\t\t0x08030000\n+/* What bpf_keyring_init() grants at boot. */\n+#define BPF_KEYRING_PERM_INITIAL\t0x082f0000\n+\n+static void bpf_keyring_sealed(void)\n+{\n+\tstatic const __u8 junk[64] = {};\n+\tstruct gen_loader_fixture f;\n+\tint serial, key, fd;\n+\n+\tif (keyring_unsealed_boot()) {\n+\t\tprintf(\"%s:SKIP:the bpf keyring was unsealed at boot\\n\", __func__);\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\tserial = bpf_keyring_lookup(NULL);\n+\tif (serial \u003e= 0) {\n+\t\tASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID,\n+\t\t\t\t KEY_SPEC_BPF_KEYRING, 0), serial,\n+\t\t\t \"KEY_SPEC_BPF_KEYRING resolves to the bpf keyring\");\n+\t\tkey = syscall(__NR_add_key, \"user\", \"sealprobe\", \"x\", 1,\n+\t\t\t KEY_SPEC_BPF_KEYRING);\n+\t\tif (key \u003e= 0)\n+\t\t\tsyscall(__NR_keyctl, KEYCTL_UNLINK, key,\n+\t\t\t\tKEY_SPEC_BPF_KEYRING);\n+\t\tASSERT_EQ(key \u003c 0 ? -errno : 0, -EPERM,\n+\t\t\t \"nothing links into a sealed keyring\");\n+\t}\n+\tif (gen_loader_fixture_init(\u0026f) == 0) {\n+\t\tfd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,\n+\t\t\t\t sizeof(junk), KEY_SPEC_BPF_KEYRING, 0);\n+\t\tASSERT_EQ(fd, -ENOKEY, \"sealed bpf keyring rejected\");\n+\t\tif (fd \u003e= 0)\n+\t\t\tclose(fd);\n+\t}\n+\tgen_loader_fixture_fini(\u0026f);\n+}\n+\n+static int try_load(const struct gen_loader_fixture *f, const void *sig,\n+\t\t __u32 sig_sz, __s32 keyring_id, char *log_buf, __u32 log_sz)\n+{\n+\tint map_fd, prog_fd;\n+\n+\tmap_fd = setup_meta_map(f);\n+\tif (!ASSERT_OK_FD(map_fd, \"meta_map\"))\n+\t\treturn map_fd;\n+\tprog_fd = load_loader_log(f-\u003egopts.insns, f-\u003egopts.insns_sz, map_fd,\n+\t\t\t\t sig, sig_sz, keyring_id, 1, log_buf, log_sz);\n+\tclose(map_fd);\n+\tif (prog_fd \u003e= 0)\n+\t\tclose(prog_fd);\n+\treturn prog_fd;\n+}\n+\n+/*\n+ * This needs bpf.keyring_unsealed=1 on the guest kernel command line, which\n+ * vmtest.sh can pass via KERNEL_CMDLINE_EXTRA. There is no way to unseal the\n+ * keyring from here, so without it the test skips. It also only works once\n+ * per boot, as restricting a keyring cannot be undone.\n+ */\n+static void bpf_keyring_provisioned(void)\n+{\n+\tchar dir_tmpl[] = \"/tmp/bpfkeyringXXXXXX\";\n+\tchar bad_tmpl[] = \"/tmp/bpfkeyringbadXXXXXX\";\n+\t__u8 *sig = NULL, *bad = NULL, *buf = NULL;\n+\tint serial, err;\n+\tint nr_keys = 0, der_fd = -1;\n+\tstruct gen_loader_fixture f;\n+\t__u32 sig_sz = 8192, bad_sz;\n+\tbool have_fixture = false;\n+\tchar *dir, *bad_dir = NULL;\n+\tchar log_buf[1024] = {};\n+\tchar path[PATH_MAX];\n+\t__u8 der[4096];\n+\tssize_t der_sz;\n+\n+\tserial = bpf_keyring_lookup(\u0026nr_keys);\n+\tif (serial \u003c 0) {\n+\t\tprintf(\"%s:SKIP:no bpf keyring (needs CONFIG_KEYS)\\n\", __func__);\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\tif (nr_keys != 0) {\n+\t\tprintf(\"%s:SKIP:the bpf keyring has already been provisioned\\n\",\n+\t\t __func__);\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\tdir = mkdtemp(dir_tmpl);\n+\tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n+\t\treturn;\n+\tif (!ASSERT_OK(run_setup(\"genkey\", dir), \"verify_sig_setup genkey\"))\n+\t\tgoto rmdir;\n+\n+\tsnprintf(path, sizeof(path), \"%s/signing_key.der\", dir);\n+\tder_fd = open(path, O_RDONLY);\n+\tif (!ASSERT_OK_FD(der_fd, \"open signing_key.der\"))\n+\t\tgoto rmdir;\n+\tder_sz = read(der_fd, der, sizeof(der));\n+\tclose(der_fd);\n+\tif (!ASSERT_GT(der_sz, 0, \"read signing_key.der\"))\n+\t\tgoto rmdir;\n+\n+\tASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),\n+\t\t serial, \"KEY_SPEC_BPF_KEYRING resolves to the bpf keyring\");\n+\n+\terr = syscall(__NR_add_key, \"asymmetric\", \"\", der, (size_t)der_sz,\n+\t\t KEY_SPEC_BPF_KEYRING);\n+\tif (err \u003c 0 \u0026\u0026 errno == EPERM) {\n+\t\tprintf(\"%s:SKIP:the bpf keyring is sealed, need bpf.keyring_unsealed=1\\n\",\n+\t\t __func__);\n+\t\ttest__skip();\n+\t\tgoto rmdir;\n+\t}\n+\tif (!ASSERT_GE(err, 0, \"add the signing key to the bpf keyring\"))\n+\t\tgoto rmdir;\n+\n+\tsig = malloc(sig_sz);\n+\tif (!ASSERT_OK_PTR(sig, \"sig buf\"))\n+\t\tgoto out;\n+\thave_fixture = true;\n+\tif (gen_loader_fixture_init(\u0026f) != 0)\n+\t\tgoto out;\n+\n+\tbuf = malloc((size_t)f.gopts.insns_sz + f.data_sz);\n+\tif (!ASSERT_OK_PTR(buf, \"signbuf\"))\n+\t\tgoto out;\n+\tmemcpy(buf, f.gopts.insns, f.gopts.insns_sz);\n+\tmemcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);\n+\tif (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig,\n+\t\t\t\t\u0026sig_sz), \"sign insns||metadata\"))\n+\t\tgoto out;\n+\n+\tASSERT_EQ(try_load(\u0026f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),\n+\t\t -ENOKEY, \"unrestricted keyring still not consulted\");\n+\n+\tASSERT_EQ(try_load(\u0026f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),\n+\t\t -EPERM, \"caller-supplied keyring refused before provisioning\");\n+\n+\tif (!ASSERT_OK(syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING,\n+\t\t\t KEY_SPEC_BPF_KEYRING, NULL, NULL),\n+\t\t \"restrict bpf keyring\"))\n+\t\tgoto out;\n+\n+\tif (!ASSERT_OK_FD(try_load(\u0026f, sig, sig_sz, KEY_SPEC_BPF_KEYRING,\n+\t\t\t\t NULL, 0),\n+\t\t\t \"load signed by a key in the .bpf keyring\"))\n+\t\tgoto out;\n+\n+\tbad_dir = mkdtemp(bad_tmpl);\n+\tif (!ASSERT_OK_PTR(bad_dir, \"mkdtemp unenrolled\"))\n+\t\tgoto out;\n+\tif (!ASSERT_OK(run_setup(\"genkey\", bad_dir), \"verify_sig_setup genkey unenrolled\"))\n+\t\tgoto out;\n+\tbad_sz = 8192;\n+\tbad = malloc(bad_sz);\n+\tif (!ASSERT_OK_PTR(bad, \"bad sig buf\"))\n+\t\tgoto out;\n+\tif (!ASSERT_OK(sign_buf(bad_dir, buf, f.gopts.insns_sz + f.data_sz, bad,\n+\t\t\t\t\u0026bad_sz), \"sign with an unenrolled key\"))\n+\t\tgoto out;\n+\n+\tASSERT_EQ(try_load(\u0026f, bad, bad_sz, KEY_SPEC_BPF_KEYRING, log_buf,\n+\t\t\t sizeof(log_buf)), -ENOKEY,\n+\t\t \"key outside the bpf keyring refused\");\n+\tASSERT_HAS_SUBSTR(log_buf, \"signature verification failed\",\n+\t\t\t \"the bpf keyring was consulted\");\n+\n+\tf.blob[0] ^= 0xff;\n+\terr = try_load(\u0026f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0);\n+\tf.blob[0] ^= 0xff;\n+\tASSERT_EQ(err, -EKEYREJECTED, \"tampered metadata refused\");\n+\n+\tASSERT_EQ(try_load(\u0026f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),\n+\t\t -EPERM, \"caller-supplied keyring refused once .bpf is in use\");\n+\n+\terr = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);\n+\tASSERT_EQ(err, -ENOENT, \"keyring writable while the user bits are there\");\n+\n+\terr = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_LOCKED);\n+\tif (!ASSERT_OK(err, \"drop the user bits on the bpf keyring\"))\n+\t\tgoto out;\n+\n+\tASSERT_OK_FD(try_load(\u0026f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),\n+\t\t \"load still verified against the locked keyring\");\n+\tASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),\n+\t\t -EACCES, \"the special id grants no rights of its own\");\n+\n+\terr = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);\n+\tASSERT_EQ(err, -EACCES, \"unlink refused\");\n+\terr = keyctl_ret(KEYCTL_CLEAR, serial, 0);\n+\tASSERT_EQ(err, -EACCES, \"clear refused\");\n+\terr = keyctl_ret(KEYCTL_REVOKE, serial, 0);\n+\tASSERT_EQ(err, -EACCES, \"revoke refused\");\n+\terr = keyctl_ret(KEYCTL_INVALIDATE, serial, 0);\n+\tASSERT_EQ(err, -EACCES, \"invalidate refused\");\n+\terr = keyctl_ret(KEYCTL_SET_TIMEOUT, serial, 1);\n+\tASSERT_EQ(err, -EACCES, \"timeout refused\");\n+\terr = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_INITIAL);\n+\tASSERT_EQ(err, -EACCES, \"the bits cannot be granted back\");\n+\n+\tASSERT_EQ(bpf_keyring_lookup(\u0026nr_keys), serial, \"keyring still there\");\n+\tASSERT_EQ(nr_keys, 1, \"the enrolled key survived\");\n+out:\n+\tif (have_fixture)\n+\t\tgen_loader_fixture_fini(\u0026f);\n+\tgenkey_dir_fini(bad_dir);\n+\tfree(buf);\n+\tfree(bad);\n+\tfree(sig);\n+rmdir:\n+\tgenkey_dir_fini(dir);\n+}\n+\n /*\n * A signed loader must ignore ctx-supplied map dimensions: the host cannot\n * resize a signed program's maps via the loader ctx. Drive a one-map program\n@@ -831,7 +1139,7 @@ static void signature_authenticates_insns(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -931,7 +1239,7 @@ static void signature_authenticates_metadata(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -1267,7 +1575,7 @@ static void lsm_signature_verdict(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\tgoto out;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\tdir = NULL;\n \t\tgoto out;\n@@ -1450,7 +1758,7 @@ static void loadtime_verify(struct bpf_object *obj, int expect_maps)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -1524,6 +1832,89 @@ static void loadtime_with_map(void)\n \ttest_signed_loader_map__destroy(skel);\n }\n \n+/*\n+ * End-to-end signed load with a post-quantum key. ML-DSA (FIPS-204) is wired\n+ * through the X.509 and PKCS#7 parsers, and BPF reaches them via\n+ * verify_pkcs7_signature() without knowing the algorithm, so an ML-DSA key in\n+ * the keyring should verify an ML-DSA signed program with no BPF-side work.\n+ */\n+static void mldsa_signed_load(void)\n+{\n+\tchar dir_tmpl[] = \"/tmp/bpfmldsaXXXXXX\";\n+\tint map_fd = -1, prog_fd = -1, err;\n+\t__u8 *sig = NULL, *buf = NULL;\n+\tstruct gen_loader_fixture f;\n+\tbool have_fixture = false;\n+\t__u32 sig_sz = 16384;\n+\tchar *dir;\n+\n+\tsyscall(__NR_request_key, \"keyring\", \"_uid.0\", NULL,\n+\t\tKEY_SPEC_SESSION_KEYRING);\n+\tdir = mkdtemp(dir_tmpl);\n+\tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n+\t\treturn;\n+\n+\terr = run_setup(\"setup-mldsa\", dir);\n+\tif (err == SETUP_SKIP) {\n+\t\tprintf(\"%s:SKIP:no working ML-DSA signing, set SELFTESTS_VERBOSE=1\\n\",\n+\t\t __func__);\n+\t\ttest__skip();\n+\t\tgenkey_dir_fini(dir);\n+\t\treturn;\n+\t}\n+\tif (!ASSERT_OK(err, \"verify_sig_setup setup-mldsa\")) {\n+\t\tgenkey_dir_fini(dir);\n+\t\treturn;\n+\t}\n+\n+\tsig = malloc(sig_sz);\n+\tif (!ASSERT_OK_PTR(sig, \"sig buf\"))\n+\t\tgoto out;\n+\thave_fixture = true;\n+\tif (gen_loader_fixture_init(\u0026f) != 0)\n+\t\tgoto out;\n+\n+\tbuf = malloc((size_t)f.gopts.insns_sz + f.data_sz);\n+\tif (!ASSERT_OK_PTR(buf, \"signbuf\"))\n+\t\tgoto out;\n+\tmemcpy(buf, f.gopts.insns, f.gopts.insns_sz);\n+\tmemcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);\n+\n+\t/*\n+\t * ML-DSA hashes the message itself, but openssl before 4.0 cannot\n+\t * produce a CMS message without signedAttrs for it, and with those in\n+\t * play only SHA-512 is permitted for the messageDigest attribute.\n+\t */\n+\tif (!ASSERT_OK(sign_buf_digest(dir, buf, f.gopts.insns_sz + f.data_sz,\n+\t\t\t\t sig, \u0026sig_sz, \"sha512\"),\n+\t\t \"sign insns||metadata with ML-DSA\"))\n+\t\tgoto out;\n+\n+\t/*\n+\t * Guard against the setup silently handing back some other key type:\n+\t * an RSA or ECDSA signature is a few hundred bytes, where an ML-DSA-87\n+\t * one cannot be smaller than the raw signature it carries.\n+\t */\n+\tASSERT_GT(sig_sz, MLDSA87_SIGNATURE_SIZE, \"ML-DSA-87 signature size\");\n+\n+\tmap_fd = setup_meta_map(\u0026f);\n+\tif (!ASSERT_OK_FD(map_fd, \"meta_map\"))\n+\t\tgoto out;\n+\tprog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,\n+\t\t\t sig_sz, KEY_SPEC_SESSION_KEYRING, 1);\n+\tASSERT_OK_FD(prog_fd, \"ML-DSA signed loader load\");\n+out:\n+\tif (prog_fd \u003e= 0)\n+\t\tclose(prog_fd);\n+\tif (map_fd \u003e= 0)\n+\t\tclose(map_fd);\n+\tif (have_fixture)\n+\t\tgen_loader_fixture_fini(\u0026f);\n+\tfree(buf);\n+\tfree(sig);\n+\trun_setup(\"cleanup\", dir);\n+}\n+\n /*\n * A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL\n * program with no fd_array is signed over its instructions alone: the kernel\n@@ -1548,7 +1939,7 @@ static void signed_no_fd_array(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -1619,7 +2010,7 @@ static void signed_map_by_fd_rejected(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\tgoto out_map;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\tgoto out_map;\n \t}\n@@ -1681,7 +2072,7 @@ static void signed_sparse_fd_array_rejected(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\tgoto out_map;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\tgoto out_map;\n \t}\n@@ -1735,7 +2126,7 @@ static void signed_module_kfunc_rejected(void)\n \tdir = mkdtemp(dir_tmpl);\n \tif (!ASSERT_OK_PTR(dir, \"mkdtemp\"))\n \t\treturn;\n-\tif (!ASSERT_OK(run_setup(\"setup\", dir), \"verify_sig_setup\")) {\n+\tif (!ASSERT_OK(run_setup(\"setup-rsa\", dir), \"verify_sig_setup\")) {\n \t\trmdir(dir);\n \t\treturn;\n \t}\n@@ -1777,64 +2168,71 @@ static void signed_module_kfunc_rejected(void)\n \trun_setup(\"cleanup\", dir);\n }\n \n+enum subtest_boot {\n+\tBOOT_ANY,\n+\tBOOT_SEALED,\n+\tBOOT_UNSEALED,\n+};\n+\n+static const struct {\n+\tconst char *name;\n+\tvoid (*fn)(void);\n+\tenum subtest_boot boot;\n+} subtests[] = {\n+\t{ \"loadtime_no_map\", loadtime_no_map, BOOT_SEALED },\n+\t{ \"loadtime_with_map\", loadtime_with_map, BOOT_SEALED },\n+\t{ \"metadata_match\", metadata_match, BOOT_ANY },\n+\t{ \"signature_enforced\", signature_enforced, BOOT_SEALED },\n+\t{ \"signed_nonexcl_fd_array_rejected\", signed_nonexcl_fd_array_rejected, BOOT_SEALED },\n+\t{ \"signed_unfrozen_fd_array_rejected\", signed_unfrozen_fd_array_rejected, BOOT_SEALED },\n+\t{ \"signed_nonarray_fd_array_rejected\", signed_nonarray_fd_array_rejected, BOOT_SEALED },\n+\t{ \"signed_btf_fd_array_rejected\", signed_btf_fd_array_rejected, BOOT_ANY },\n+\t{ \"signed_module_kfunc_rejected\", signed_module_kfunc_rejected, BOOT_SEALED },\n+\t{ \"signature_failure_logs\", signature_failure_logs, BOOT_SEALED },\n+\t{ \"signature_too_large\", signature_too_large, BOOT_ANY },\n+\t{ \"signature_zero_size\", signature_zero_size, BOOT_ANY },\n+\t{ \"signature_bad_keyring\", signature_bad_keyring, BOOT_SEALED },\n+\t{ \"bpf_keyring_sealed\", bpf_keyring_sealed, BOOT_ANY },\n+\t{ \"mldsa_signed_load\", mldsa_signed_load, BOOT_SEALED },\n+\t{ \"metadata_ctx_max_entries_ignored\", metadata_ctx_max_entries_ignored, BOOT_ANY },\n+\t{ \"metadata_ctx_initial_value_ignored\", metadata_ctx_initial_value_ignored, BOOT_ANY },\n+\t{ \"signature_authenticates_insns\", signature_authenticates_insns, BOOT_SEALED },\n+\t{ \"signature_authenticates_metadata\", signature_authenticates_metadata, BOOT_SEALED },\n+\t{ \"hash_requires_frozen\", hash_requires_frozen, BOOT_ANY },\n+\t{ \"no_update_after_freeze\", no_update_after_freeze, BOOT_ANY },\n+\t{ \"freeze_writable_mmap\", freeze_writable_mmap, BOOT_ANY },\n+\t{ \"no_writable_mmap_frozen\", no_writable_mmap_frozen, BOOT_ANY },\n+\t{ \"map_hash_matches_libbpf\", map_hash_matches_libbpf, BOOT_ANY },\n+\t{ \"map_hash_multi_element\", map_hash_multi_element, BOOT_ANY },\n+\t{ \"map_hash_bad_size\", map_hash_bad_size, BOOT_ANY },\n+\t{ \"map_hash_unsupported_type\", map_hash_unsupported_type, BOOT_ANY },\n+\t{ \"lsm_signature_verdict\", lsm_signature_verdict, BOOT_SEALED },\n+\t{ \"signed_no_fd_array\", signed_no_fd_array, BOOT_SEALED },\n+\t{ \"signed_map_by_fd_rejected\", signed_map_by_fd_rejected, BOOT_SEALED },\n+\t{ \"signed_sparse_fd_array_rejected\", signed_sparse_fd_array_rejected, BOOT_SEALED },\n+\t{ \"bpf_keyring_provisioned\", bpf_keyring_provisioned, BOOT_UNSEALED },\n+};\n+\n void test_signed_loader(void)\n {\n-\tif (test__start_subtest(\"loadtime_no_map\"))\n-\t\tloadtime_no_map();\n-\tif (test__start_subtest(\"loadtime_with_map\"))\n-\t\tloadtime_with_map();\n-\tif (test__start_subtest(\"metadata_match\"))\n-\t\tmetadata_match();\n-\tif (test__start_subtest(\"signature_enforced\"))\n-\t\tsignature_enforced();\n-\tif (test__start_subtest(\"signed_nonexcl_fd_array_rejected\"))\n-\t\tsigned_nonexcl_fd_array_rejected();\n-\tif (test__start_subtest(\"signed_unfrozen_fd_array_rejected\"))\n-\t\tsigned_unfrozen_fd_array_rejected();\n-\tif (test__start_subtest(\"signed_nonarray_fd_array_rejected\"))\n-\t\tsigned_nonarray_fd_array_rejected();\n-\tif (test__start_subtest(\"signed_btf_fd_array_rejected\"))\n-\t\tsigned_btf_fd_array_rejected();\n-\tif (test__start_subtest(\"signed_module_kfunc_rejected\"))\n-\t\tsigned_module_kfunc_rejected();\n-\tif (test__start_subtest(\"signature_failure_logs\"))\n-\t\tsignature_failure_logs();\n-\tif (test__start_subtest(\"signature_too_large\"))\n-\t\tsignature_too_large();\n-\tif (test__start_subtest(\"signature_zero_size\"))\n-\t\tsignature_zero_size();\n-\tif (test__start_subtest(\"signature_bad_keyring\"))\n-\t\tsignature_bad_keyring();\n-\tif (test__start_subtest(\"metadata_ctx_max_entries_ignored\"))\n-\t\tmetadata_ctx_max_entries_ignored();\n-\tif (test__start_subtest(\"metadata_ctx_initial_value_ignored\"))\n-\t\tmetadata_ctx_initial_value_ignored();\n-\tif (test__start_subtest(\"signature_authenticates_insns\"))\n-\t\tsignature_authenticates_insns();\n-\tif (test__start_subtest(\"signature_authenticates_metadata\"))\n-\t\tsignature_authenticates_metadata();\n-\tif (test__start_subtest(\"hash_requires_frozen\"))\n-\t\thash_requires_frozen();\n-\tif (test__start_subtest(\"no_update_after_freeze\"))\n-\t\tno_update_after_freeze();\n-\tif (test__start_subtest(\"freeze_writable_mmap\"))\n-\t\tfreeze_writable_mmap();\n-\tif (test__start_subtest(\"no_writable_mmap_frozen\"))\n-\t\tno_writable_mmap_frozen();\n-\tif (test__start_subtest(\"map_hash_matches_libbpf\"))\n-\t\tmap_hash_matches_libbpf();\n-\tif (test__start_subtest(\"map_hash_multi_element\"))\n-\t\tmap_hash_multi_element();\n-\tif (test__start_subtest(\"map_hash_bad_size\"))\n-\t\tmap_hash_bad_size();\n-\tif (test__start_subtest(\"map_hash_unsupported_type\"))\n-\t\tmap_hash_unsupported_type();\n-\tif (test__start_subtest(\"lsm_signature_verdict\"))\n-\t\tlsm_signature_verdict();\n-\tif (test__start_subtest(\"signed_no_fd_array\"))\n-\t\tsigned_no_fd_array();\n-\tif (test__start_subtest(\"signed_map_by_fd_rejected\"))\n-\t\tsigned_map_by_fd_rejected();\n-\tif (test__start_subtest(\"signed_sparse_fd_array_rejected\"))\n-\t\tsigned_sparse_fd_array_rejected();\n+\tbool unsealed = keyring_unsealed_boot();\n+\tunsigned int i;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(subtests); i++) {\n+\t\tif (!test__start_subtest(subtests[i].name))\n+\t\t\tcontinue;\n+\t\tif (subtests[i].boot == BOOT_SEALED \u0026\u0026 unsealed) {\n+\t\t\tprintf(\"%s:SKIP:needs a boot without bpf.keyring_unsealed=1\\n\",\n+\t\t\t subtests[i].name);\n+\t\t\ttest__skip();\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (subtests[i].boot == BOOT_UNSEALED \u0026\u0026 !unsealed) {\n+\t\t\tprintf(\"%s:SKIP:needs bpf.keyring_unsealed=1\\n\",\n+\t\t\t subtests[i].name);\n+\t\t\ttest__skip();\n+\t\t\tcontinue;\n+\t\t}\n+\t\tsubtests[i].fn();\n+\t}\n }\ndiff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c\nindex f327feb8e38c3..12b146d205d75 100644\n--- a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c\n+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c\n@@ -257,7 +257,7 @@ static void test_verify_pkcs7_sig_from_map(void)\n \tif (!ASSERT_OK_PTR(tmp_dir, \"mkdtemp\"))\n \t\treturn;\n \n-\tret = _run_setup_process(tmp_dir, \"setup\");\n+\tret = _run_setup_process(tmp_dir, \"setup-rsa\");\n \tif (!ASSERT_OK(ret, \"_run_setup_process\"))\n \t\tgoto close_prog;\n \n@@ -458,7 +458,7 @@ static void test_pkcs7_sig_fsverity(void)\n \tsnprintf(data_path, PATH_MAX, \"%s/data-file\", tmp_dir);\n \tsnprintf(sig_path, PATH_MAX, \"%s/sig-file\", tmp_dir);\n \n-\tret = _run_setup_process(tmp_dir, \"setup\");\n+\tret = _run_setup_process(tmp_dir, \"setup-rsa\");\n \tif (!ASSERT_OK(ret, \"_run_setup_process\"))\n \t\tgoto out;\n \ndiff --git a/tools/testing/selftests/bpf/verify_sig_setup.sh b/tools/testing/selftests/bpf/verify_sig_setup.sh\nindex 09179fb551f09..ba5921e4b12d2 100755\n--- a/tools/testing/selftests/bpf/verify_sig_setup.sh\n+++ b/tools/testing/selftests/bpf/verify_sig_setup.sh\n@@ -28,7 +28,7 @@ authorityKeyIdentifier=keyid\n \n usage()\n {\n-\techo \"Usage: $0 \u003csetup|cleanup \u003cexisting_tmp_dir\u003e\"\n+\techo \"Usage: $0 \u003csetup-rsa|setup-mldsa|cleanup \u003cexisting_tmp_dir\u003e\"\n \texit 1\n }\n \n@@ -47,7 +47,7 @@ genkey()\n \t\t${tmp_dir}/signing_key.der -outform der\n }\n \n-setup()\n+setup_rsa()\n {\n \tlocal tmp_dir=\"$1\"\n \n@@ -57,6 +57,57 @@ setup()\n \tkeyctl link $key_id $keyring_id\n }\n \n+mldsa_supported()\n+{\n+\tlocal tmp_dir=\"$1\"\n+\n+\tgenkey_mldsa \"${tmp_dir}\" || return 1\n+\t: \u003e ${tmp_dir}/probe\n+\t# Same digest as the caller signs with, see sign_buf_digest().\n+\t./sign-file -d sha512 ${tmp_dir}/signing_key.pem \\\n+\t\t${tmp_dir}/signing_key.pem ${tmp_dir}/probe || return 1\n+\trm -f ${tmp_dir}/probe ${tmp_dir}/probe.p7s\n+}\n+\n+genkey_mldsa()\n+{\n+\tlocal tmp_dir=\"$1\"\n+\n+\techo \"${x509_genkey_content}\" \u003e ${tmp_dir}/x509.genkey\n+\n+\t# No -\u003cdigest\u003e here: ML-DSA hashes the message itself, so openssl\n+\t# ignores an explicit digest for it.\n+\topenssl req -new -nodes -utf8 -days 36500 \\\n+\t\t\t-batch -x509 -newkey ML-DSA-87 \\\n+\t\t\t-config ${tmp_dir}/x509.genkey \\\n+\t\t\t-outform PEM -out ${tmp_dir}/signing_key.pem \\\n+\t\t\t-keyout ${tmp_dir}/signing_key.pem 2\u003e\u00261\n+\n+\topenssl x509 -in ${tmp_dir}/signing_key.pem -out \\\n+\t\t${tmp_dir}/signing_key.der -outform der\n+}\n+\n+mldsa_skip()\n+{\n+\tlocal tmp_dir=\"$1\"\n+\n+\trm -f ${tmp_dir}/x509.genkey ${tmp_dir}/signing_key.pem \\\n+\t\t${tmp_dir}/signing_key.der ${tmp_dir}/probe \\\n+\t\t${tmp_dir}/probe.p7s\n+\texit 77\n+}\n+\n+setup_mldsa()\n+{\n+\tlocal tmp_dir=\"$1\"\n+\n+\tmldsa_supported \"${tmp_dir}\" || mldsa_skip \"${tmp_dir}\"\n+\tkey_id=$(cat ${tmp_dir}/signing_key.der |\n+\t\t keyctl padd asymmetric ebpf_testing_key @s)\n+\tkeyring_id=$(keyctl newring ebpf_testing_keyring @s)\n+\tkeyctl link $key_id $keyring_id\n+}\n+\n cleanup() {\n \tlocal tmp_dir=\"$1\"\n \n@@ -91,7 +142,7 @@ catch()\n \tlocal exit_code=\"$1\"\n \tlocal log_file=\"$2\"\n \n-\tif [[ \"${exit_code}\" -ne 0 ]]; then\n+\tif [[ \"${exit_code}\" -ne 0 \u0026\u0026 \"${exit_code}\" -ne 77 ]]; then\n \t\tcat \"${log_file}\" \u003e\u00263\n \tfi\n \n@@ -108,8 +159,10 @@ main()\n \n \t[[ ! -d \"${tmp_dir}\" ]] \u0026\u0026 echo \"Directory ${tmp_dir} doesn't exist\" \u0026\u0026 exit 1\n \n-\tif [[ \"${action}\" == \"setup\" ]]; then\n-\t\tsetup \"${tmp_dir}\"\n+\tif [[ \"${action}\" == \"setup-rsa\" ]]; then\n+\t\tsetup_rsa \"${tmp_dir}\"\n+\telif [[ \"${action}\" == \"setup-mldsa\" ]]; then\n+\t\tsetup_mldsa \"${tmp_dir}\"\n \telif [[ \"${action}\" == \"genkey\" ]]; then\n \t\tgenkey \"${tmp_dir}\"\n \telif [[ \"${action}\" == \"cleanup\" ]]; then\ndiff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh\nindex 6a3d026d76bd6..e7e0b419a0b87 100755\n--- a/tools/testing/selftests/bpf/vmtest.sh\n+++ b/tools/testing/selftests/bpf/vmtest.sh\n@@ -107,6 +107,14 @@ Options:\n \t-s)\t\tInstead of powering off the VM, start an interactive\n \t\t\tshell. If \u003ccommand\u003e is specified, the shell runs after\n \t\t\tthe command finishes executing\n+\n+Environment variables:\n+\n+\tKERNEL_CMDLINE_EXTRA\n+\t\t\tExtra arguments to append to the guest kernel command\n+\t\t\tline, for tests that need a boot-time setting. e.g:\n+\n+\t KERNEL_CMDLINE_EXTRA=\"bpf.keyring_unsealed=1\" $0 -- ./test_progs -t signed_loader\n EOF\n }\n \n@@ -286,6 +294,12 @@ EOF\n \t\tQEMU_FLAGS=(\"${HOST_FLAGS[@]}\")\n \tfi\n \n+\tlocal kernel_cmdline=\"root=/dev/vda rw console=${QEMU_CONSOLE}\"\n+\n+\tif [[ -n \"${KERNEL_CMDLINE_EXTRA:-}\" ]]; then\n+\t\tkernel_cmdline+=\" ${KERNEL_CMDLINE_EXTRA}\"\n+\tfi\n+\n \t${QEMU_BINARY} \\\n \t\t-nodefaults \\\n \t\t-display none \\\n@@ -294,7 +308,7 @@ EOF\n \t\t-m 4G \\\n \t\t-drive file=\"${rootfs_img}\",format=raw,index=1,media=disk,if=virtio,cache=none \\\n \t\t-kernel \"${kernel_bzimage}\" \\\n-\t\t-append \"root=/dev/vda rw console=${QEMU_CONSOLE}\"\n+\t\t-append \"${kernel_cmdline}\"\n }\n \n copy_logs()\n"
} |
| 2/1 | 2026/08/28 18:30 | agent | patch-evaluator |
2mModel:core Args: null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"KEYS",
"SYSTEM_DATA_VERIFICATION",
"ASYMMETRIC_KEY_TYPE",
"CRYPTO_MLDSA",
"CRYPTO_SHA512",
"CRYPTO_SHA256",
"CRYPTO_RSA"
],
"FocusSymbols": [
"bpf_lookup_keyring",
"bpf_keyring_enforced",
"key_register_bpf_keyring",
"lookup_user_key",
"bpf_check",
"bpf_verify_pkcs7_signature"
],
"Reasoning": "The patch introduces a dedicated BPF keyring (.bpf) selectable via KEY_SPEC_BPF_KEYRING (-9), new kernel logic for lookup, restriction, and enforcement during BPF program verification, and support for ML-DSA signed program verification. These changes modify core BPF program loading and key management logic reachable from userspace.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit e339e4245c3ea6b0a065a66a530e37d1313633f6
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 28 18:30:36 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index e4643634a9b1b..e70fb15757d40 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -667,6 +667,22 @@ Kernel parameters
See Documentation/admin-guide/bootconfig.rst
+ bpf.keyring_unsealed=
+ [BPF] When set to 1, leave the bpf keyring unsealed
+ after initialization so that userspace can provision
+ keys. Once the keyring is restricted it becomes active
+ and can be used for BPF program signature verification.
+
+ Setting this also means that the bpf keyring is the
+ only non-system keyring a loader may select for the
+ rest of the boot: caller-supplied user/session
+ keyrings are refused with -EPERM, whether or not
+ provisioning actually completed. The system keyrings
+ stay selectable. Leaving it unset keeps the prior
+ behaviour, where a caller-supplied keyring is allowed.
+
+ See Documentation/bpf/signing.rst
+
bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards)
bttv.radio= Most important insmod options are available as
kernel args too.
diff --git a/Documentation/bpf/signing.rst b/Documentation/bpf/signing.rst
index e73eaaebd8b15..27fdad3df96f1 100644
--- a/Documentation/bpf/signing.rst
+++ b/Documentation/bpf/signing.rst
@@ -254,21 +254,24 @@ returned. Only after the program has fully loaded, at the next hook
(``security_bpf_prog()``), does ``BPF_SIG_VERIFIED`` carry its full meaning:
validly signed *and* fully verified.
-A more realistic admission policy than "is it signed at all": accept programs
-signed by a system keyring, accept a user-keyring signature only if the
-key/keyring it was verified against is on an explicit allowlist, and emit a
-tamper-evident record of every decision so that even denied attempts are
-auditable. (Illustrative - error checking elided.)
+A more realistic admission policy than "is it signed at all": base trust in
+the bpf keyring or an explicit allowlist of a caller-supplied staging key/
+keyring, and emit a record of every decision including denied attempts.
+(Illustrative - error checking elided.)
.. code-block:: c
- /* Serials of user keys/keyrings we additionally trust. */
+ /*
+ * Serials of caller-supplied keyrings we are willing to stage. Empty
+ * on a system that has committed to the bpf keyring, where the kernel
+ * refuses them anyway.
+ */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __s32); /* keyring_serial */
__type(value, __u8);
__uint(max_entries, 64);
- } trusted_user_keys SEC(".maps");
+ } staging_keys SEC(".maps");
/* Audit stream consumed by a userspace logger. */
struct {
@@ -291,11 +294,18 @@ auditable. (Illustrative - error checking elided.)
if (kernel)
return 0; /* trust in-kernel loads */
- if (verdict != BPF_SIG_VERIFIED)
+ if (verdict != BPF_SIG_VERIFIED) {
ret = -EPERM; /* must be validly signed */
- else if (ktype == BPF_SIG_KEYRING_USER &&
- !bpf_map_lookup_elem(&trusted_user_keys, &serial))
- ret = -EPERM; /* key/keyring not allowlisted */
+ } else switch (ktype) {
+ case BPF_SIG_KEYRING_BPF:
+ break;
+ case BPF_SIG_KEYRING_USER:
+ if (!bpf_map_lookup_elem(&staging_keys, &serial))
+ ret = -EPERM;
+ break;
+ default:
+ ret = -EPERM; /* keyring not in policy */
+ }
d = bpf_ringbuf_reserve(&audit, sizeof(*d), 0);
if (d) {
@@ -309,6 +319,11 @@ auditable. (Illustrative - error checking elided.)
return ret;
}
+Such a policy is what makes a caller-supplied keyring usable at all on a system
+that does not boot with ``bpf.keyring_unsealed=1``: the allowlist bounds which
+staged keys count, and the LSM itself has to protect them from being tampered
+with.
+
Observing a verified load: ``security_bpf_prog()``
--------------------------------------------------
@@ -381,8 +396,9 @@ that verdict covered all of its exclusive maps, rejecting any that did not - so
a deny-by-default admission policy needs no second enforcement point. Use
``security_bpf_prog()`` to record or finally gate the verified programs once
they carry an id. The ``verdict``, ``keyring_type`` and ``keyring_serial`` fields
-let a policy distinguish, for example, "verified and signed by a builtin key"
-from "verified by a user key". A policy LSM such as IPE could consume the same
+let a policy distinguish "verified against the operator's bpf keyring" from
+"verified against a keyring the loader supplied itself", which is the
+distinction that matters most. A policy LSM such as IPE could consume the same
hooks to enforce system policy without writing any BPF, though none implements
this today.
@@ -390,33 +406,158 @@ Keyrings
========
``keyring_id`` selects the trusted keyring the PKCS#7 signature is verified
-against. The well-known ids ``0`` (builtin), ``VERIFY_USE_SECONDARY_KEYRING``
-and ``VERIFY_USE_PLATFORM_KEYRING`` select the corresponding system keyrings;
-any other value is treated as the serial of a user/session key or keyring.
-The keyring is looked up first, before the signature bytes are examined, so a
-signature naming a non-existent keyring is rejected up front, and a failed
-verification aborts the load - so a program that loads successfully with a
-signature always has consistent keyring fields recorded.
+against. Four values are well-known; anything else is resolved as a caller-
+supplied key or keyring, named either by its serial or by one of the other
+``KEY_SPEC_*`` ids:
+
+.. list-table::
+ :header-rows: 1
+
+ * - ``keyring_id``
+ - Keyring
+ * - ``0``
+ - builtin trusted keyring
+ * - ``VERIFY_USE_SECONDARY_KEYRING`` (``1``)
+ - secondary trusted keyring
+ * - ``VERIFY_USE_PLATFORM_KEYRING`` (``2``)
+ - platform keyring
+ * - ``KEY_SPEC_BPF_KEYRING`` (``-9``)
+ - the bpf keyring
+ * - anything else
+ - a caller-supplied user/session key or keyring
+
+The keyring is resolved first, before the signature bytes are examined, so a
+signature naming a keyring that cannot be used is rejected up front, and a
+failed verification aborts the load - a program that loads successfully with
+a signature therefore always has consistent keyring fields recorded.
+
+The bpf keyring
+---------------
+
+A system keyring needs a kernel rebuild or a vouched-for enrollment to rotate a
+key, and grants BPF-signing trust to keys trusted for everything else in the
+kernel too. A caller-supplied keyring, at the other extreme, is filled by the
+very process that loads the program and so carries no trust of its own.
+
+The bpf keyring fills that gap and is the trust anchor which a signed BPF
+deployment should be built on top of: a keyring named ``.bpf``, selected with
+``KEY_SPEC_BPF_KEYRING``, that an operator provisions at boot with a key
+scoped to BPF program loading and nothing else in the kernel's trust hierarchy.
+It is owned by the operator rather than by the loader, and rotatable across a
+reboot without touching the kernel image. It is modelled after the dm-verity
+keyring (see ``dm_verity.keyring_unsealed=``) and provisioned the same way: an
+initrd runs the ``keyctl`` steps below before handing off to the rootfs.
+
+Provisioning
+~~~~~~~~~~~~
+
+The keyring is created during ``late_initcall`` and is **sealed empty** by
+default: it carries a reject-all restriction, so no key can ever be added and
+``KEY_SPEC_BPF_KEYRING`` fails with ``-ENOKEY`` for the whole boot.
+
+``bpf.keyring_unsealed=1`` leaves it unrestricted at init so the initrd can
+provision it. The keyring is not linked into any process keyring, but the
+``KEY_SPEC_BPF_KEYRING`` special key id addresses it directly, so its serial
+does not have to be looked up first. Steps would be as follows::
+
+ keyctl padd asymmetric "" -9 < signing_key.der
+ keyctl restrict_keyring -9
+
+Both steps are required: the keyring is consulted only once it is **non-empty
+and restricted**. An unrestricted keyring is ignored even when it holds keys,
+so a half-provisioned keyring is inert rather than a weaker trust anchor, and a
+load naming it fails with ``-ENOKEY`` and a verifier log. Restricting cannot
+be undone.
+
+More than one key is enrolled by repeating the ``keyctl padd`` step; the
+restriction is applied once, after the last of them::
+
+ for key in /etc/bpf/keys/*.der; do
+ keyctl padd asymmetric "" -9 < $key
+ done
+
+ keyctl restrict_keyring -9
+ keyctl show -9
+
+The restriction bounds what can be added, never what can be taken away. A key
+that is already enrolled can still be unlinked, and the keyring cleared or
+revoked, by anything running as root. That does not weaken the anchor, since
+a keyring left empty is no longer consulted and a load naming it fails with
+``-ENOKEY``, but it does take signed loading out until the next boot. Dropping
+the user permissions the keyring no longer needs would close that; as a third
+step in the initrd::
+
+ keyctl setperm -9 0x08030000
+
+What remains is ``KEY_POS_SEARCH`` for the in-kernel search during verification,
+plus ``KEY_USR_VIEW`` and ``KEY_USR_READ`` so the keyring stays visible in
+``/proc/keys``. ``KEY_SPEC_BPF_KEYRING`` names the keyring but conveys no rights
+of its own - the keyring is not possessed by anyone, so the ``KEY_POS_*`` bits
+are only ever reached by the in-kernel search, and userspace is left with what
+the ``KEY_USR_*`` bits grant. Once they are dropped, addressing it by the serial
+``/proc/keys`` reports gets no further than the special id does.
+
+Provisioning has to complete before control passes to the rootfs. The keyring
+takes any number of keys for as long as it is unrestricted, so it is the
+restriction that bounds the enrolled set, not the first enrollment. Before
+the initrd hands off control, it must therefore restrict the keyring after
+the last enrollment.
+
+Enforcement
+~~~~~~~~~~~
+
+``bpf.keyring_unsealed=1`` states that the bpf keyring is *the* trust anchor for
+this boot, so it does more than unseal. From the first program load onwards a
+caller-supplied user/session keyring is refused with ``-EPERM`` and a verifier
+log message, whether or not provisioning ever completed. The system keyrings
+stay selectable.
+
+Enforcement is readable at ``/sys/module/bpf/parameters/keyring_unsealed``, and
+read-only there: the flag is ``__ro_after_init`` behind a 0444 parameter. It is
+also derived from the boot flag rather than from the keyring's runtime state,
+so there is no window early in boot during which a caller-supplied keyring is
+still accepted.
+
+Caller-supplied keyrings are for staging
+----------------------------------------
+
+A ``keyring_id`` naming a user or session key or keyring is a *staging*
+mechanism, not a trust anchor: it is filled by the same userspace that loads the
+program, so verifying against it establishes only that the loader signed what it
+loaded. Its purpose is to let software installed onto a running system - whose
+signing key is not enrolled anywhere yet - run signed until that key reaches the
+bpf keyring on the next boot.
+
+A system that has committed to the bpf keyring refuses this path outright (see
+`Enforcement`_). A system that has not can still allow it, but a policy must
+never treat ``BPF_SIG_KEYRING_USER`` as equivalent to the bpf or system
+keyrings; it should allowlist the specific serials it is willing to stage and
+pair that with a BPF LSM policy protecting those keys from tampering, as in
+`Enforcement via LSMs`_.
+
+Recorded fields
+---------------
Two fields are recorded in ``prog->aux->sig`` for an LSM to inspect:
``keyring_type`` (``enum bpf_sig_keyring``)
Classified purely from ``keyring_id`` whenever the program is signed:
``BPF_SIG_KEYRING_BUILTIN``, ``_SECONDARY``, ``_PLATFORM`` for the system
- keyrings, or ``_USER`` for a user/session keyring. It is
- ``BPF_SIG_KEYRING_NONE`` for an unsigned program.
+ keyrings, ``_BPF`` for the bpf keyring, or ``_USER`` for a caller-supplied
+ user/session keyring. It is ``BPF_SIG_KEYRING_NONE`` for an unsigned
+ program.
``keyring_serial`` (``s32``)
Set **only** on a successful verification, to the serial of the
- **user/session key or keyring** that ``keyring_id`` resolved to - the
+ **caller-supplied key or keyring** that ``keyring_id`` resolved to - the
object the signature was verified against, not the individual asymmetric
key inside it that matched the signer. Passing
``KEY_SPEC_SESSION_KEYRING``, for example, records the session keyring's
- serial. The system keyrings are trusted as a whole and expose no serial
- here, so the serial is ``0`` for builtin, secondary and platform
- signatures, and ``0`` for unsigned programs. In other words, a non-zero
- ``keyring_serial`` is exactly "verified against the user key/keyring with
- this serial".
+ serial. The system keyrings and the bpf keyring are trusted as a whole and
+ expose no serial here, so the serial is ``0`` for them, and ``0`` for
+ unsigned programs. A non-zero ``keyring_serial`` is therefore exactly
+ "verified against the caller-supplied key/keyring with this serial", which
+ is exactly the case a policy has to scrutinise.
.. list-table::
:header-rows: 1
@@ -436,16 +577,51 @@ Two fields are recorded in ``prog->aux->sig`` for an LSM to inspect:
* - ``VERIFY_USE_PLATFORM_KEYRING``
- ``BPF_SIG_KEYRING_PLATFORM``
- ``0``
- * - other (a user/session key serial)
+ * - ``KEY_SPEC_BPF_KEYRING``
+ - ``BPF_SIG_KEYRING_BPF``
+ - ``0``
+ * - other (a caller-supplied key serial)
- ``BPF_SIG_KEYRING_USER``
- serial of the resolved key/keyring
-Producing a signed object
-==========================
+Producing and loading a signed object
+=====================================
+
+Generating a signing key
+------------------------
+
+Signing is algorithm agnostic: the algorithm comes from the X.509 certificate
+and the PKCS#7 ``SignerInfo``. Anything the X.509 and PKCS#7 parsers understand
+works with no BPF-side change. Both examples below read the certificate request
+config from ``x509.genkey``, for which ``certs/x509.genkey`` serves as a
+template (see Documentation/admin-guide/module-signing.rst). RSA::
+
+ openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
+ -config x509.genkey -outform PEM \
+ -out signing_key.pem -keyout signing_key.pem
+ openssl x509 -in signing_key.pem -outform der -out signing_key.der
+
+ML-DSA-87 (FIPS-204), which needs openssl 3.5 or later, and both
+``CONFIG_CRYPTO_MLDSA`` and ``CONFIG_CRYPTO_SHA512`` in the kernel - the latter
+for the signedAttrs digest described below, which ``CONFIG_CRYPTO_MLDSA`` does
+not select. Note the absence of a digest option: ML-DSA hashes the message
+itself, so openssl ignores an explicit digest for it::
+
+ openssl req -new -nodes -utf8 -days 36500 -batch -x509 \
+ -newkey ML-DSA-87 -config x509.genkey -outform PEM \
+ -out signing_key.pem -keyout signing_key.pem
+ openssl x509 -in signing_key.pem -outform der -out signing_key.der
+
+``bpftool`` handles the following internally: openssl before 4.0 cannot combine
+ML-DSA with ``CMS_NOATTR``, so it falls back to signedAttrs, where only SHA-512
+is permitted. This mirrors what module signing does as well.
+
+Signing
+-------
``bpftool`` generates and signs a light skeleton in one step::
- bpftool gen skeleton -L -S -k <private_key.pem> -i <certificate.x509> \
+ bpftool gen skeleton -L -S -k signing_key.pem -i signing_key.der \
obj.bpf.o > obj.lskel.h
``-L`` selects the light-skeleton (``gen_loader``) backend and ``-S`` enables
@@ -454,12 +630,36 @@ signing; ``-k`` and ``-i`` supply the signing key and its X.509 certificate.
reconstructs - and also computes ``excl_prog_hash`` as the digest of the loader
instructions so the metadata map can be bound to the loader. The signature and
hash are embedded in the generated header; the certificate is used only for
-signing and is not included. Loading the skeleton performs the
-create/populate/freeze/load sequence described above.
+signing and is not included.
+
+Loading
+-------
+
+The generated skeleton exposes ``keyring_id``, which selects the keyring the
+kernel verifies against. Set it between open and load; loading then performs
+the create/populate/freeze/load sequence described above::
-At runtime the trusted public key must be present in the chosen keyring (for
-example added to the session keyring, or built into the kernel's builtin trusted
-keyring) for verification to succeed.
+ struct obj *skel = obj__open();
+
+ skel->keyring_id = KEY_SPEC_BPF_KEYRING;
+ err = obj__load(skel);
+
+For the staging case the same object is loaded against a keyring the caller
+populated itself, which only works on a system that has not set
+``bpf.keyring_unsealed=1``::
+
+ /*
+ * Staging only: this keyring is under the loader's own control and
+ * carries no trust of its own. See "Caller-supplied keyrings are for
+ * staging".
+ */
+ key_id = add_key("asymmetric", "", der, der_sz, KEY_SPEC_SESSION_KEYRING);
+ skel->keyring_id = KEY_SPEC_SESSION_KEYRING;
+ err = obj__load(skel);
+
+Either way the trusted public key must already be in the chosen keyring for
+verification to succeed. For the bpf keyring that enrollment happens once at
+boot, see `Provisioning`_.
UAPI reference
==============
@@ -487,6 +687,13 @@ UAPI reference
The map content is not hashed separately at all - it is covered, as bytes,
by the program signature.
+Kernel command line:
+
+``bpf.keyring_unsealed=``
+ Set to ``1`` to leave the bpf keyring unsealed for provisioning, and to make
+ it the only non-system keyring a loader may select for the rest of the boot
+ (see `The bpf keyring`_).
+
Notes and limitations
======================
@@ -495,3 +702,5 @@ Notes and limitations
exceed it.
- The metadata container is a single-element array map, accessed through
``map_direct_value_addr``.
+- The bpf keyring needs ``CONFIG_KEYS``; without it there is no bpf keyring
+ and ``KEY_SPEC_BPF_KEYRING`` never resolves.
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index c2027688be3e4..3a7eb2185c354 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1735,6 +1735,7 @@ enum bpf_sig_keyring {
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
+ BPF_SIG_KEYRING_BPF,
};
struct bpf_prog_aux {
@@ -3819,6 +3820,8 @@ struct bpf_key {
#if defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL)
struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);
struct bpf_key *bpf_lookup_system_key(u64 id);
+struct bpf_key *bpf_lookup_keyring(void);
+bool bpf_keyring_enforced(void);
void bpf_key_put(struct bpf_key *bkey);
int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,
const struct bpf_dynptr *sig_p,
@@ -3839,6 +3842,16 @@ static inline struct bpf_key *bpf_lookup_system_key(u64 id)
return NULL;
}
+static inline struct bpf_key *bpf_lookup_keyring(void)
+{
+ return NULL;
+}
+
+static inline bool bpf_keyring_enforced(void)
+{
+ return false;
+}
+
static inline void bpf_key_put(struct bpf_key *bkey)
{
}
diff --git a/include/linux/key.h b/include/linux/key.h
index 81b8f05c68985..bd10fe45819db 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -440,6 +440,8 @@ extern key_ref_t keyring_search(key_ref_t keyring,
extern int keyring_restrict(key_ref_t keyring, const char *type,
const char *restriction);
+extern void key_register_bpf_keyring(struct key *keyring);
+
extern struct key *key_lookup(key_serial_t id);
static inline key_serial_t key_serial(const struct key *key)
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 4c8884eea8084..fa85b9760391a 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -24,6 +24,7 @@
#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed request_key auth key */
#define KEY_SPEC_REQUESTOR_KEYRING -8 /* - key ID for request_key() dest keyring */
+#define KEY_SPEC_BPF_KEYRING -9 /* - key ID for the BPF-specific keyring */
/* request-key default keyrings */
#define KEY_REQKEY_DEFL_NO_CHANGE -1
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 90255d80e5be6..9a92c348bbda6 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_BPF_SYSCALL) += offload.o
obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o
obj-$(CONFIG_BPF_SYSCALL) += tcx.o
endif
+ifeq ($(CONFIG_KEYS),y)
+obj-$(CONFIG_BPF_SYSCALL) += keys.o
+endif
ifeq ($(CONFIG_PERF_EVENTS),y)
obj-$(CONFIG_BPF_SYSCALL) += stackmap.o
endif
diff --git a/kernel/bpf/keys.c b/kernel/bpf/keys.c
new file mode 100644
index 0000000000000..bffc356839ac5
--- /dev/null
+++ b/kernel/bpf/keys.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Isovalent */
+
+#include <linux/bpf.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/key.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "bpf."
+
+static struct key *bpf_keyring;
+
+static bool bpf_keyring_unsealed __ro_after_init;
+module_param_named(keyring_unsealed, bpf_keyring_unsealed, bool, 0444);
+MODULE_PARM_DESC(keyring_unsealed, "Leave the bpf keyring unsealed");
+
+bool bpf_keyring_enforced(void)
+{
+ return bpf_keyring_unsealed;
+}
+
+struct bpf_key *bpf_lookup_keyring(void)
+{
+ struct bpf_key *bkey;
+
+ if (!bpf_keyring)
+ return NULL;
+ if (!READ_ONCE(bpf_keyring->keys.nr_leaves_on_tree) ||
+ !READ_ONCE(bpf_keyring->restrict_link))
+ return NULL;
+
+ bkey = kmalloc_obj(*bkey);
+ if (!bkey)
+ return NULL;
+
+ bkey->key = bpf_keyring;
+ bkey->has_ref = false;
+ return bkey;
+}
+
+static int __init bpf_keyring_init(void)
+{
+ struct key *keyring;
+
+ keyring = keyring_alloc(".bpf",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH |
+ KEY_USR_SETATTR, KEY_ALLOC_NOT_IN_QUOTA,
+ NULL, NULL);
+ if (IS_ERR(keyring)) {
+ pr_err("bpf: cannot allocate bpf keyring: %ld\n",
+ PTR_ERR(keyring));
+ return 0;
+ }
+ if (!bpf_keyring_unsealed &&
+ keyring_restrict(make_key_ref(keyring, true), NULL, NULL)) {
+ pr_err("bpf: cannot seal bpf keyring\n");
+ key_revoke(keyring);
+ key_put(keyring);
+ return 0;
+ }
+
+ bpf_keyring = keyring;
+ key_register_bpf_keyring(keyring);
+ return 0;
+}
+late_initcall(bpf_keyring_init);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e036ae20bf6b9..d50a135466bb6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -24,6 +24,7 @@
#include <linux/bpf_lsm.h>
#include <linux/security.h>
#include <linux/verification.h>
+#include <linux/keyctl.h>
#include <linux/btf_ids.h>
#include <linux/poison.h>
#include <linux/module.h>
@@ -20972,6 +20973,14 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return 0;
}
+/*
+ * Upper bound on the PKCS#7 signature blob passed with a program. Comfortably
+ * above the largest signature the kernel can verify, and far below anything
+ * that would make rejecting a load expensive. Deliberately a fixed number so
+ * that what the syscall accepts does not depend on PAGE_SIZE.
+ */
+#define BPF_PROG_MAX_SIGNATURE_SIZE (64 * 1024)
+
static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
{
switch (keyring_id) {
@@ -20981,6 +20990,8 @@ static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
return BPF_SIG_KEYRING_SECONDARY;
case (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:
return BPF_SIG_KEYRING_PLATFORM;
+ case KEY_SPEC_BPF_KEYRING:
+ return BPF_SIG_KEYRING_BPF;
default:
return BPF_SIG_KEYRING_USER;
}
@@ -21009,18 +21020,25 @@ static int bpf_prog_verify_signature(struct bpf_verifier_env *env,
u64 data_sz;
int err = 0;
- /*
- * Don't attempt to use kmalloc_large or vmalloc for signatures.
- * Practical signature for BPF program should be below this limit.
- */
if (!attr->signature_size ||
- attr->signature_size > KMALLOC_MAX_CACHE_SIZE)
+ attr->signature_size > BPF_PROG_MAX_SIGNATURE_SIZE)
return -EINVAL;
- if (system_keyring_id_check(attr->keyring_id) == 0)
+
+ if (system_keyring_id_check(attr->keyring_id) == 0) {
key = bpf_lookup_system_key(attr->keyring_id);
- else
+ } else if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ key = bpf_lookup_keyring();
+ } else if (bpf_keyring_enforced()) {
+ verbose(env, "caller-supplied keyring refused, use bpf keyring\n");
+ return -EPERM;
+ } else {
key = bpf_lookup_user_key(attr->keyring_id, 0);
+ }
if (!key) {
+ if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ verbose(env, "the bpf keyring is empty or has not been restricted\n");
+ return -ENOKEY;
+ }
verbose(env, "cannot resolve signing keyring with keyring_id %d\n",
attr->keyring_id);
return -EINVAL;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a63c46bb2d148..44358388e3959 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -22,6 +22,9 @@
/* Session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex);
+/* BPF keyring reachable through KEY_SPEC_BPF_KEYRING */
+static struct key *bpf_keyring __ro_after_init;
+
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = REFCOUNT_INIT(3),
@@ -590,6 +593,20 @@ bool lookup_user_key_possessed(const struct key *key,
return key == match_data->raw_data;
}
+/**
+ * key_register_bpf_keyring - Publish the BPF keyring for KEY_SPEC_BPF_KEYRING
+ * @keyring: The keyring to publish
+ *
+ * Make @keyring reachable by userspace through the KEY_SPEC_BPF_KEYRING
+ * special key ID, so that provisioning it does not require scraping its
+ * serial out of /proc/keys first. Called once, from an initcall, and never
+ * undone.
+ */
+void key_register_bpf_keyring(struct key *keyring)
+{
+ bpf_keyring = keyring;
+}
+
/*
* Look up a key ID given us by userspace with a given permissions mask to get
* the key it refers to.
@@ -741,6 +758,14 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_ref = make_key_ref(key, 1);
break;
+ case KEY_SPEC_BPF_KEYRING:
+ key = bpf_keyring;
+ if (!key)
+ goto error;
+ __key_get(key);
+ key_ref = make_key_ref(key, 0);
+ break;
+
default:
key_ref = ERR_PTR(-EINVAL);
if (id < 1)
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 78b6e0ebb85d8..9315a1db1f7c2 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -57,7 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
})
#define ERR_MAX_LEN 1024
-#define MAX_SIG_SIZE 4096
+#define MAX_SIG_SIZE 16384
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
index 88726a6db6d0e..adbfd4ae5c021 100644
--- a/tools/bpf/bpftool/sign.c
+++ b/tools/bpf/bpftool/sign.c
@@ -130,6 +130,12 @@ __u32 register_session_key(const char *key_der_path)
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
{
+ unsigned int signer_flags = CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
+#ifdef CMS_NO_SIGNING_TIME
+ CMS_NO_SIGNING_TIME |
+#endif
+ CMS_USE_KEYID | CMS_NOATTR;
+ const EVP_MD *cms_digest = EVP_sha256();
BIO *bd_in = NULL, *bd_out = NULL;
EVP_PKEY *private_key = NULL;
CMS_ContentInfo *cms = NULL;
@@ -167,6 +173,20 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
goto cleanup;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L
+ if (EVP_PKEY_is_a(private_key, "ML-DSA-44") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-65") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-87")) {
+ /*
+ * See workaround in 0ad9a71933e7 ("modsign: Enable ML-DSA
+ * module signing"). Kernel only accepts sha512, see also
+ * 8bbdeb7a25b4 ("pkcs7, x509: Add ML-DSA support").
+ */
+ signer_flags &= ~CMS_NOATTR;
+ cms_digest = EVP_sha512();
+ }
+#endif
+
cms = CMS_sign(NULL, NULL, NULL, NULL,
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
CMS_STREAM);
@@ -175,9 +195,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
goto cleanup;
}
- if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
- CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
- CMS_USE_KEYID | CMS_NOATTR)) {
+ if (!CMS_add1_signer(cms, x509, private_key, cms_digest, signer_flags)) {
err = -EINVAL;
goto cleanup;
}
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 5f1a3bfc0569f..05b3ee64290fa 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -663,7 +663,7 @@ $(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
}) && \
rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
-$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
+$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) $(VERIFICATION_CERT) | $(TRUNNER_OUTPUT)
$(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \
$$(RM) $$@; \
printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index ea7044f30adc3..2f79688dcf7ce 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -12,7 +12,9 @@ CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set
CONFIG_CGROUP_BPF=y
CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MLDSA=y
CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_USER_API=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
diff --git a/tools/testing/selftests/bpf/prog_tests/signed_loader.c b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
index 77381d345435c..a0f93756e717b 100644
--- a/tools/testing/selftests/bpf/prog_tests/signed_loader.c
+++ b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
@@ -32,11 +32,22 @@ enum {
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
+ BPF_SIG_KEYRING_BPF,
};
-static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
- const void *sig, __u32 sig_sz, __s32 keyring_id,
- __u32 fd_array_cnt)
+#ifndef KEY_SPEC_BPF_KEYRING
+#define KEY_SPEC_BPF_KEYRING -9
+#endif
+
+/* verify_sig_setup.sh exits with this when openssl cannot do ML-DSA. */
+#define SETUP_SKIP (-77)
+
+/* FIPS-204 ML-DSA-87 signature size, see include/crypto/mldsa.h. */
+#define MLDSA87_SIGNATURE_SIZE 4627
+
+static int load_loader_log(const void *insns, __u32 insns_sz, int map_fd,
+ const void *sig, __u32 sig_sz, __s32 keyring_id,
+ __u32 fd_array_cnt, char *log_buf, __u32 log_sz)
{
union bpf_attr attr;
int fd;
@@ -48,18 +59,31 @@ static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
attr.license = ptr_to_u64("Dual BSD/GPL");
attr.prog_flags = BPF_F_SLEEPABLE;
attr.fd_array = ptr_to_u64(&map_fd);
+ attr.fd_array_cnt = fd_array_cnt;
if (sig) {
attr.signature = ptr_to_u64(sig);
attr.signature_size = sig_sz;
attr.keyring_id = keyring_id;
}
- attr.fd_array_cnt = fd_array_cnt;
+ if (log_buf) {
+ attr.log_level = 1;
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_sz;
+ }
memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
offsetofend(union bpf_attr, keyring_id));
return fd < 0 ? -errno : fd;
}
+static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
+ const void *sig, __u32 sig_sz, __s32 keyring_id,
+ __u32 fd_array_cnt)
+{
+ return load_loader_log(insns, insns_sz, map_fd, sig, sig_sz, keyring_id,
+ fd_array_cnt, NULL, 0);
+}
+
static int run_gen_loader(const void *insns, __u32 insns_sz,
const void *data, __u32 data_sz,
const void *excl, __u32 excl_sz,
@@ -156,12 +180,30 @@ static int run_setup(const char *cmd, const char *dir)
}
if (waitpid(pid, &status, 0) < 0)
return -errno;
- return (WIFEXITED(status) &&
- WEXITSTATUS(status) == 0) ? 0 : -EINVAL;
+ if (!WIFEXITED(status))
+ return -EINVAL;
+ return -WEXITSTATUS(status);
}
-static int sign_buf(const char *dir, const void *buf, __u32 len,
- void *sig, __u32 *sig_sz)
+static void genkey_dir_fini(const char *dir)
+{
+ static const char * const files[] = {
+ "signing_key.der", "signing_key.pem", "x509.genkey",
+ };
+ char path[PATH_MAX];
+ size_t i;
+
+ if (!dir)
+ return;
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ snprintf(path, sizeof(path), "%s/%s", dir, files[i]);
+ unlink(path);
+ }
+ rmdir(dir);
+}
+
+static int sign_buf_digest(const char *dir, const void *buf, __u32 len,
+ void *sig, __u32 *sig_sz, const char *digest)
{
char data_tmpl[PATH_MAX], key[PATH_MAX];
char sigpath[PATH_MAX + sizeof(".p7s")];
@@ -176,6 +218,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
fd = mkstemp(data_tmpl);
if (fd < 0)
return -errno;
+ snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
if (write(fd, buf, len) != (ssize_t)len) {
close(fd);
ret = -EIO;
@@ -190,7 +233,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
}
if (pid == 0) {
snprintf(key, sizeof(key), "%s/signing_key.pem", dir);
- execlp("./sign-file", "./sign-file", "-d", "sha256",
+ execlp("./sign-file", "./sign-file", "-d", digest,
key, key, data_tmpl, NULL);
exit(1);
}
@@ -200,34 +243,38 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
goto out;
}
- snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
if (stat(sigpath, &st) < 0) {
ret = -errno;
goto out;
}
if (st.st_size > (off_t)*sig_sz) {
ret = -E2BIG;
- goto out_sig;
+ goto out;
}
fd = open(sigpath, O_RDONLY);
if (fd < 0) {
ret = -errno;
- goto out_sig;
+ goto out;
}
if (read(fd, sig, st.st_size) != st.st_size) {
close(fd);
ret = -EIO;
- goto out_sig;
+ goto out;
}
close(fd);
*sig_sz = st.st_size;
-out_sig:
- unlink(sigpath);
out:
+ unlink(sigpath);
unlink(data_tmpl);
return ret;
}
+static int sign_buf(const char *dir, const void *buf, __u32 len,
+ void *sig, __u32 *sig_sz)
+{
+ return sign_buf_digest(dir, buf, len, sig, sig_sz, "sha256");
+}
+
struct gen_loader_fixture {
struct test_signed_loader *skel;
struct gen_loader_opts gopts;
@@ -457,7 +504,7 @@ static void signed_btf_fd_array_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -529,7 +576,6 @@ static void signature_failure_logs(void)
static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
char log_buf[1024] = {};
struct gen_loader_fixture f;
- union bpf_attr attr;
int fd;
if (gen_loader_fixture_init(&f) == 0) {
@@ -538,22 +584,9 @@ static void signature_failure_logs(void)
* failure is reported through the verifier log. A present-but-
* invalid signature is rejected and the log says why.
*/
- memset(&attr, 0, sizeof(attr));
- attr.prog_type = BPF_PROG_TYPE_SYSCALL;
- attr.insns = ptr_to_u64(f.gopts.insns);
- attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn);
- attr.license = ptr_to_u64("Dual BSD/GPL");
- attr.prog_flags = BPF_F_SLEEPABLE;
- attr.signature = ptr_to_u64(junk);
- attr.signature_size = sizeof(junk);
- attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
- attr.log_level = 1;
- attr.log_buf = ptr_to_u64(log_buf);
- attr.log_size = sizeof(log_buf);
- memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
-
- fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
- offsetofend(union bpf_attr, keyring_id));
+ fd = load_loader_log(f.gopts.insns, f.gopts.insns_sz, -1, junk,
+ sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0,
+ log_buf, sizeof(log_buf));
ASSERT_LT(fd, 0, "invalid signature rejected at load");
if (fd >= 0)
close(fd);
@@ -571,8 +604,9 @@ static void signature_too_large(void)
if (gen_loader_fixture_init(&f) == 0) {
/*
- * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE)
- * is rejected before the buffer is read.
+ * signature_size beyond the kernel's bound
+ * (BPF_PROG_MAX_SIGNATURE_SIZE) is rejected before the buffer
+ * is read.
*/
fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
64 << 20, KEY_SPEC_SESSION_KEYRING, 0);
@@ -626,6 +660,280 @@ static void signature_bad_keyring(void)
gen_loader_fixture_fini(&f);
}
+static bool keyring_unsealed_boot(void)
+{
+ char val = 0;
+ int fd;
+
+ fd = open("/sys/module/bpf/parameters/keyring_unsealed", O_RDONLY);
+ if (fd < 0)
+ return false;
+ if (read(fd, &val, 1) != 1)
+ val = 0;
+ close(fd);
+ return val == 'Y' || val == '1';
+}
+
+static int bpf_keyring_lookup(int *nr_keys)
+{
+ char line[512], type[32], desc[64];
+ int serial = -ENOENT;
+ FILE *f;
+
+ f = fopen("/proc/keys", "r");
+ if (!f)
+ return -errno;
+
+ while (fgets(line, sizeof(line), f)) {
+ unsigned int hex;
+ char *sum;
+
+ if (sscanf(line, "%x %*s %*s %*s %*s %*s %*s %31s %63s",
+ &hex, type, desc) != 3)
+ continue;
+ if (strcmp(type, "keyring") || strcmp(desc, ".bpf:"))
+ continue;
+
+ serial = (int)hex;
+ if (nr_keys) {
+ sum = strstr(line, ".bpf: ");
+ *nr_keys = !sum || !strncmp(sum + 6, "empty", 5) ?
+ 0 : atoi(sum + 6);
+ }
+ break;
+ }
+ fclose(f);
+ return serial;
+}
+
+static long keyctl_ret(int cmd, unsigned long arg2, unsigned long arg3)
+{
+ long ret = syscall(__NR_keyctl, cmd, arg2, arg3);
+
+ return ret < 0 ? -errno : ret;
+}
+
+/*
+ * What the bpf keyring still needs once it got provisioned: KEY_POS_SEARCH
+ * for the in-kernel search during verification, and the user view/read bits
+ * so it stays visible in /proc/keys, rest is dropped so the enrolled is
+ * therefore final.
+ */
+#define BPF_KEYRING_PERM_LOCKED 0x08030000
+/* What bpf_keyring_init() grants at boot. */
+#define BPF_KEYRING_PERM_INITIAL 0x082f0000
+
+static void bpf_keyring_sealed(void)
+{
+ static const __u8 junk[64] = {};
+ struct gen_loader_fixture f;
+ int serial, key, fd;
+
+ if (keyring_unsealed_boot()) {
+ printf("%s:SKIP:the bpf keyring was unsealed at boot\n", __func__);
+ test__skip();
+ return;
+ }
+ serial = bpf_keyring_lookup(NULL);
+ if (serial >= 0) {
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID,
+ KEY_SPEC_BPF_KEYRING, 0), serial,
+ "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring");
+ key = syscall(__NR_add_key, "user", "sealprobe", "x", 1,
+ KEY_SPEC_BPF_KEYRING);
+ if (key >= 0)
+ syscall(__NR_keyctl, KEYCTL_UNLINK, key,
+ KEY_SPEC_BPF_KEYRING);
+ ASSERT_EQ(key < 0 ? -errno : 0, -EPERM,
+ "nothing links into a sealed keyring");
+ }
+ if (gen_loader_fixture_init(&f) == 0) {
+ fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
+ sizeof(junk), KEY_SPEC_BPF_KEYRING, 0);
+ ASSERT_EQ(fd, -ENOKEY, "sealed bpf keyring rejected");
+ if (fd >= 0)
+ close(fd);
+ }
+ gen_loader_fixture_fini(&f);
+}
+
+static int try_load(const struct gen_loader_fixture *f, const void *sig,
+ __u32 sig_sz, __s32 keyring_id, char *log_buf, __u32 log_sz)
+{
+ int map_fd, prog_fd;
+
+ map_fd = setup_meta_map(f);
+ if (!ASSERT_OK_FD(map_fd, "meta_map"))
+ return map_fd;
+ prog_fd = load_loader_log(f->gopts.insns, f->gopts.insns_sz, map_fd,
+ sig, sig_sz, keyring_id, 1, log_buf, log_sz);
+ close(map_fd);
+ if (prog_fd >= 0)
+ close(prog_fd);
+ return prog_fd;
+}
+
+/*
+ * This needs bpf.keyring_unsealed=1 on the guest kernel command line, which
+ * vmtest.sh can pass via KERNEL_CMDLINE_EXTRA. There is no way to unseal the
+ * keyring from here, so without it the test skips. It also only works once
+ * per boot, as restricting a keyring cannot be undone.
+ */
+static void bpf_keyring_provisioned(void)
+{
+ char dir_tmpl[] = "/tmp/bpfkeyringXXXXXX";
+ char bad_tmpl[] = "/tmp/bpfkeyringbadXXXXXX";
+ __u8 *sig = NULL, *bad = NULL, *buf = NULL;
+ int serial, err;
+ int nr_keys = 0, der_fd = -1;
+ struct gen_loader_fixture f;
+ __u32 sig_sz = 8192, bad_sz;
+ bool have_fixture = false;
+ char *dir, *bad_dir = NULL;
+ char log_buf[1024] = {};
+ char path[PATH_MAX];
+ __u8 der[4096];
+ ssize_t der_sz;
+
+ serial = bpf_keyring_lookup(&nr_keys);
+ if (serial < 0) {
+ printf("%s:SKIP:no bpf keyring (needs CONFIG_KEYS)\n", __func__);
+ test__skip();
+ return;
+ }
+ if (nr_keys != 0) {
+ printf("%s:SKIP:the bpf keyring has already been provisioned\n",
+ __func__);
+ test__skip();
+ return;
+ }
+
+ dir = mkdtemp(dir_tmpl);
+ if (!ASSERT_OK_PTR(dir, "mkdtemp"))
+ return;
+ if (!ASSERT_OK(run_setup("genkey", dir), "verify_sig_setup genkey"))
+ goto rmdir;
+
+ snprintf(path, sizeof(path), "%s/signing_key.der", dir);
+ der_fd = open(path, O_RDONLY);
+ if (!ASSERT_OK_FD(der_fd, "open signing_key.der"))
+ goto rmdir;
+ der_sz = read(der_fd, der, sizeof(der));
+ close(der_fd);
+ if (!ASSERT_GT(der_sz, 0, "read signing_key.der"))
+ goto rmdir;
+
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),
+ serial, "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring");
+
+ err = syscall(__NR_add_key, "asymmetric", "", der, (size_t)der_sz,
+ KEY_SPEC_BPF_KEYRING);
+ if (err < 0 && errno == EPERM) {
+ printf("%s:SKIP:the bpf keyring is sealed, need bpf.keyring_unsealed=1\n",
+ __func__);
+ test__skip();
+ goto rmdir;
+ }
+ if (!ASSERT_GE(err, 0, "add the signing key to the bpf keyring"))
+ goto rmdir;
+
+ sig = malloc(sig_sz);
+ if (!ASSERT_OK_PTR(sig, "sig buf"))
+ goto out;
+ have_fixture = true;
+ if (gen_loader_fixture_init(&f) != 0)
+ goto out;
+
+ buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
+ if (!ASSERT_OK_PTR(buf, "signbuf"))
+ goto out;
+ memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
+ memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
+ if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig,
+ &sig_sz), "sign insns||metadata"))
+ goto out;
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),
+ -ENOKEY, "unrestricted keyring still not consulted");
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),
+ -EPERM, "caller-supplied keyring refused before provisioning");
+
+ if (!ASSERT_OK(syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING,
+ KEY_SPEC_BPF_KEYRING, NULL, NULL),
+ "restrict bpf keyring"))
+ goto out;
+
+ if (!ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING,
+ NULL, 0),
+ "load signed by a key in the .bpf keyring"))
+ goto out;
+
+ bad_dir = mkdtemp(bad_tmpl);
+ if (!ASSERT_OK_PTR(bad_dir, "mkdtemp unenrolled"))
+ goto out;
+ if (!ASSERT_OK(run_setup("genkey", bad_dir), "verify_sig_setup genkey unenrolled"))
+ goto out;
+ bad_sz = 8192;
+ bad = malloc(bad_sz);
+ if (!ASSERT_OK_PTR(bad, "bad sig buf"))
+ goto out;
+ if (!ASSERT_OK(sign_buf(bad_dir, buf, f.gopts.insns_sz + f.data_sz, bad,
+ &bad_sz), "sign with an unenrolled key"))
+ goto out;
+
+ ASSERT_EQ(try_load(&f, bad, bad_sz, KEY_SPEC_BPF_KEYRING, log_buf,
+ sizeof(log_buf)), -ENOKEY,
+ "key outside the bpf keyring refused");
+ ASSERT_HAS_SUBSTR(log_buf, "signature verification failed",
+ "the bpf keyring was consulted");
+
+ f.blob[0] ^= 0xff;
+ err = try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0);
+ f.blob[0] ^= 0xff;
+ ASSERT_EQ(err, -EKEYREJECTED, "tampered metadata refused");
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),
+ -EPERM, "caller-supplied keyring refused once .bpf is in use");
+
+ err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);
+ ASSERT_EQ(err, -ENOENT, "keyring writable while the user bits are there");
+
+ err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_LOCKED);
+ if (!ASSERT_OK(err, "drop the user bits on the bpf keyring"))
+ goto out;
+
+ ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),
+ "load still verified against the locked keyring");
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),
+ -EACCES, "the special id grants no rights of its own");
+
+ err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);
+ ASSERT_EQ(err, -EACCES, "unlink refused");
+ err = keyctl_ret(KEYCTL_CLEAR, serial, 0);
+ ASSERT_EQ(err, -EACCES, "clear refused");
+ err = keyctl_ret(KEYCTL_REVOKE, serial, 0);
+ ASSERT_EQ(err, -EACCES, "revoke refused");
+ err = keyctl_ret(KEYCTL_INVALIDATE, serial, 0);
+ ASSERT_EQ(err, -EACCES, "invalidate refused");
+ err = keyctl_ret(KEYCTL_SET_TIMEOUT, serial, 1);
+ ASSERT_EQ(err, -EACCES, "timeout refused");
+ err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_INITIAL);
+ ASSERT_EQ(err, -EACCES, "the bits cannot be granted back");
+
+ ASSERT_EQ(bpf_keyring_lookup(&nr_keys), serial, "keyring still there");
+ ASSERT_EQ(nr_keys, 1, "the enrolled key survived");
+out:
+ if (have_fixture)
+ gen_loader_fixture_fini(&f);
+ genkey_dir_fini(bad_dir);
+ free(buf);
+ free(bad);
+ free(sig);
+rmdir:
+ genkey_dir_fini(dir);
+}
+
/*
* A signed loader must ignore ctx-supplied map dimensions: the host cannot
* resize a signed program's maps via the loader ctx. Drive a one-map program
@@ -831,7 +1139,7 @@ static void signature_authenticates_insns(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -931,7 +1239,7 @@ static void signature_authenticates_metadata(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1267,7 +1575,7 @@ static void lsm_signature_verdict(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
dir = NULL;
goto out;
@@ -1450,7 +1758,7 @@ static void loadtime_verify(struct bpf_object *obj, int expect_maps)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1524,6 +1832,89 @@ static void loadtime_with_map(void)
test_signed_loader_map__destroy(skel);
}
+/*
+ * End-to-end signed load with a post-quantum key. ML-DSA (FIPS-204) is wired
+ * through the X.509 and PKCS#7 parsers, and BPF reaches them via
+ * verify_pkcs7_signature() without knowing the algorithm, so an ML-DSA key in
+ * the keyring should verify an ML-DSA signed program with no BPF-side work.
+ */
+static void mldsa_signed_load(void)
+{
+ char dir_tmpl[] = "/tmp/bpfmldsaXXXXXX";
+ int map_fd = -1, prog_fd = -1, err;
+ __u8 *sig = NULL, *buf = NULL;
+ struct gen_loader_fixture f;
+ bool have_fixture = false;
+ __u32 sig_sz = 16384;
+ char *dir;
+
+ syscall(__NR_request_key, "keyring", "_uid.0", NULL,
+ KEY_SPEC_SESSION_KEYRING);
+ dir = mkdtemp(dir_tmpl);
+ if (!ASSERT_OK_PTR(dir, "mkdtemp"))
+ return;
+
+ err = run_setup("setup-mldsa", dir);
+ if (err == SETUP_SKIP) {
+ printf("%s:SKIP:no working ML-DSA signing, set SELFTESTS_VERBOSE=1\n",
+ __func__);
+ test__skip();
+ genkey_dir_fini(dir);
+ return;
+ }
+ if (!ASSERT_OK(err, "verify_sig_setup setup-mldsa")) {
+ genkey_dir_fini(dir);
+ return;
+ }
+
+ sig = malloc(sig_sz);
+ if (!ASSERT_OK_PTR(sig, "sig buf"))
+ goto out;
+ have_fixture = true;
+ if (gen_loader_fixture_init(&f) != 0)
+ goto out;
+
+ buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
+ if (!ASSERT_OK_PTR(buf, "signbuf"))
+ goto out;
+ memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
+ memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
+
+ /*
+ * ML-DSA hashes the message itself, but openssl before 4.0 cannot
+ * produce a CMS message without signedAttrs for it, and with those in
+ * play only SHA-512 is permitted for the messageDigest attribute.
+ */
+ if (!ASSERT_OK(sign_buf_digest(dir, buf, f.gopts.insns_sz + f.data_sz,
+ sig, &sig_sz, "sha512"),
+ "sign insns||metadata with ML-DSA"))
+ goto out;
+
+ /*
+ * Guard against the setup silently handing back some other key type:
+ * an RSA or ECDSA signature is a few hundred bytes, where an ML-DSA-87
+ * one cannot be smaller than the raw signature it carries.
+ */
+ ASSERT_GT(sig_sz, MLDSA87_SIGNATURE_SIZE, "ML-DSA-87 signature size");
+
+ map_fd = setup_meta_map(&f);
+ if (!ASSERT_OK_FD(map_fd, "meta_map"))
+ goto out;
+ prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
+ sig_sz, KEY_SPEC_SESSION_KEYRING, 1);
+ ASSERT_OK_FD(prog_fd, "ML-DSA signed loader load");
+out:
+ if (prog_fd >= 0)
+ close(prog_fd);
+ if (map_fd >= 0)
+ close(map_fd);
+ if (have_fixture)
+ gen_loader_fixture_fini(&f);
+ free(buf);
+ free(sig);
+ run_setup("cleanup", dir);
+}
+
/*
* A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL
* program with no fd_array is signed over its instructions alone: the kernel
@@ -1548,7 +1939,7 @@ static void signed_no_fd_array(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1619,7 +2010,7 @@ static void signed_map_by_fd_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out_map;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
goto out_map;
}
@@ -1681,7 +2072,7 @@ static void signed_sparse_fd_array_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out_map;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
goto out_map;
}
@@ -1735,7 +2126,7 @@ static void signed_module_kfunc_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1777,64 +2168,71 @@ static void signed_module_kfunc_rejected(void)
run_setup("cleanup", dir);
}
+enum subtest_boot {
+ BOOT_ANY,
+ BOOT_SEALED,
+ BOOT_UNSEALED,
+};
+
+static const struct {
+ const char *name;
+ void (*fn)(void);
+ enum subtest_boot boot;
+} subtests[] = {
+ { "loadtime_no_map", loadtime_no_map, BOOT_SEALED },
+ { "loadtime_with_map", loadtime_with_map, BOOT_SEALED },
+ { "metadata_match", metadata_match, BOOT_ANY },
+ { "signature_enforced", signature_enforced, BOOT_SEALED },
+ { "signed_nonexcl_fd_array_rejected", signed_nonexcl_fd_array_rejected, BOOT_SEALED },
+ { "signed_unfrozen_fd_array_rejected", signed_unfrozen_fd_array_rejected, BOOT_SEALED },
+ { "signed_nonarray_fd_array_rejected", signed_nonarray_fd_array_rejected, BOOT_SEALED },
+ { "signed_btf_fd_array_rejected", signed_btf_fd_array_rejected, BOOT_ANY },
+ { "signed_module_kfunc_rejected", signed_module_kfunc_rejected, BOOT_SEALED },
+ { "signature_failure_logs", signature_failure_logs, BOOT_SEALED },
+ { "signature_too_large", signature_too_large, BOOT_ANY },
+ { "signature_zero_size", signature_zero_size, BOOT_ANY },
+ { "signature_bad_keyring", signature_bad_keyring, BOOT_SEALED },
+ { "bpf_keyring_sealed", bpf_keyring_sealed, BOOT_ANY },
+ { "mldsa_signed_load", mldsa_signed_load, BOOT_SEALED },
+ { "metadata_ctx_max_entries_ignored", metadata_ctx_max_entries_ignored, BOOT_ANY },
+ { "metadata_ctx_initial_value_ignored", metadata_ctx_initial_value_ignored, BOOT_ANY },
+ { "signature_authenticates_insns", signature_authenticates_insns, BOOT_SEALED },
+ { "signature_authenticates_metadata", signature_authenticates_metadata, BOOT_SEALED },
+ { "hash_requires_frozen", hash_requires_frozen, BOOT_ANY },
+ { "no_update_after_freeze", no_update_after_freeze, BOOT_ANY },
+ { "freeze_writable_mmap", freeze_writable_mmap, BOOT_ANY },
+ { "no_writable_mmap_frozen", no_writable_mmap_frozen, BOOT_ANY },
+ { "map_hash_matches_libbpf", map_hash_matches_libbpf, BOOT_ANY },
+ { "map_hash_multi_element", map_hash_multi_element, BOOT_ANY },
+ { "map_hash_bad_size", map_hash_bad_size, BOOT_ANY },
+ { "map_hash_unsupported_type", map_hash_unsupported_type, BOOT_ANY },
+ { "lsm_signature_verdict", lsm_signature_verdict, BOOT_SEALED },
+ { "signed_no_fd_array", signed_no_fd_array, BOOT_SEALED },
+ { "signed_map_by_fd_rejected", signed_map_by_fd_rejected, BOOT_SEALED },
+ { "signed_sparse_fd_array_rejected", signed_sparse_fd_array_rejected, BOOT_SEALED },
+ { "bpf_keyring_provisioned", bpf_keyring_provisioned, BOOT_UNSEALED },
+};
+
void test_signed_loader(void)
{
- if (test__start_subtest("loadtime_no_map"))
- loadtime_no_map();
- if (test__start_subtest("loadtime_with_map"))
- loadtime_with_map();
- if (test__start_subtest("metadata_match"))
- metadata_match();
- if (test__start_subtest("signature_enforced"))
- signature_enforced();
- if (test__start_subtest("signed_nonexcl_fd_array_rejected"))
- signed_nonexcl_fd_array_rejected();
- if (test__start_subtest("signed_unfrozen_fd_array_rejected"))
- signed_unfrozen_fd_array_rejected();
- if (test__start_subtest("signed_nonarray_fd_array_rejected"))
- signed_nonarray_fd_array_rejected();
- if (test__start_subtest("signed_btf_fd_array_rejected"))
- signed_btf_fd_array_rejected();
- if (test__start_subtest("signed_module_kfunc_rejected"))
- signed_module_kfunc_rejected();
- if (test__start_subtest("signature_failure_logs"))
- signature_failure_logs();
- if (test__start_subtest("signature_too_large"))
- signature_too_large();
- if (test__start_subtest("signature_zero_size"))
- signature_zero_size();
- if (test__start_subtest("signature_bad_keyring"))
- signature_bad_keyring();
- if (test__start_subtest("metadata_ctx_max_entries_ignored"))
- metadata_ctx_max_entries_ignored();
- if (test__start_subtest("metadata_ctx_initial_value_ignored"))
- metadata_ctx_initial_value_ignored();
- if (test__start_subtest("signature_authenticates_insns"))
- signature_authenticates_insns();
- if (test__start_subtest("signature_authenticates_metadata"))
- signature_authenticates_metadata();
- if (test__start_subtest("hash_requires_frozen"))
- hash_requires_frozen();
- if (test__start_subtest("no_update_after_freeze"))
- no_update_after_freeze();
- if (test__start_subtest("freeze_writable_mmap"))
- freeze_writable_mmap();
- if (test__start_subtest("no_writable_mmap_frozen"))
- no_writable_mmap_frozen();
- if (test__start_subtest("map_hash_matches_libbpf"))
- map_hash_matches_libbpf();
- if (test__start_subtest("map_hash_multi_element"))
- map_hash_multi_element();
- if (test__start_subtest("map_hash_bad_size"))
- map_hash_bad_size();
- if (test__start_subtest("map_hash_unsupported_type"))
- map_hash_unsupported_type();
- if (test__start_subtest("lsm_signature_verdict"))
- lsm_signature_verdict();
- if (test__start_subtest("signed_no_fd_array"))
- signed_no_fd_array();
- if (test__start_subtest("signed_map_by_fd_rejected"))
- signed_map_by_fd_rejected();
- if (test__start_subtest("signed_sparse_fd_array_rejected"))
- signed_sparse_fd_array_rejected();
+ bool unsealed = keyring_unsealed_boot();
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(subtests); i++) {
+ if (!test__start_subtest(subtests[i].name))
+ continue;
+ if (subtests[i].boot == BOOT_SEALED && unsealed) {
+ printf("%s:SKIP:needs a boot without bpf.keyring_unsealed=1\n",
+ subtests[i].name);
+ test__skip();
+ continue;
+ }
+ if (subtests[i].boot == BOOT_UNSEALED && !unsealed) {
+ printf("%s:SKIP:needs bpf.keyring_unsealed=1\n",
+ subtests[i].name);
+ test__skip();
+ continue;
+ }
+ subtests[i].fn();
+ }
}
diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
index f327feb8e38c3..12b146d205d75 100644
--- a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
@@ -257,7 +257,7 @@ static void test_verify_pkcs7_sig_from_map(void)
if (!ASSERT_OK_PTR(tmp_dir, "mkdtemp"))
return;
- ret = _run_setup_process(tmp_dir, "setup");
+ ret = _run_setup_process(tmp_dir, "setup-rsa");
if (!ASSERT_OK(ret, "_run_setup_process"))
goto close_prog;
@@ -458,7 +458,7 @@ static void test_pkcs7_sig_fsverity(void)
snprintf(data_path, PATH_MAX, "%s/data-file", tmp_dir);
snprintf(sig_path, PATH_MAX, "%s/sig-file", tmp_dir);
- ret = _run_setup_process(tmp_dir, "setup");
+ ret = _run_setup_process(tmp_dir, "setup-rsa");
if (!ASSERT_OK(ret, "_run_setup_process"))
goto out;
diff --git a/tools/testing/selftests/bpf/verify_sig_setup.sh b/tools/testing/selftests/bpf/verify_sig_setup.sh
index 09179fb551f09..ba5921e4b12d2 100755
--- a/tools/testing/selftests/bpf/verify_sig_setup.sh
+++ b/tools/testing/selftests/bpf/verify_sig_setup.sh
@@ -28,7 +28,7 @@ authorityKeyIdentifier=keyid
usage()
{
- echo "Usage: $0 <setup|cleanup <existing_tmp_dir>"
+ echo "Usage: $0 <setup-rsa|setup-mldsa|cleanup <existing_tmp_dir>"
exit 1
}
@@ -47,7 +47,7 @@ genkey()
${tmp_dir}/signing_key.der -outform der
}
-setup()
+setup_rsa()
{
local tmp_dir="$1"
@@ -57,6 +57,57 @@ setup()
keyctl link $key_id $keyring_id
}
+mldsa_supported()
+{
+ local tmp_dir="$1"
+
+ genkey_mldsa "${tmp_dir}" || return 1
+ : > ${tmp_dir}/probe
+ # Same digest as the caller signs with, see sign_buf_digest().
+ ./sign-file -d sha512 ${tmp_dir}/signing_key.pem \
+ ${tmp_dir}/signing_key.pem ${tmp_dir}/probe || return 1
+ rm -f ${tmp_dir}/probe ${tmp_dir}/probe.p7s
+}
+
+genkey_mldsa()
+{
+ local tmp_dir="$1"
+
+ echo "${x509_genkey_content}" > ${tmp_dir}/x509.genkey
+
+ # No -<digest> here: ML-DSA hashes the message itself, so openssl
+ # ignores an explicit digest for it.
+ openssl req -new -nodes -utf8 -days 36500 \
+ -batch -x509 -newkey ML-DSA-87 \
+ -config ${tmp_dir}/x509.genkey \
+ -outform PEM -out ${tmp_dir}/signing_key.pem \
+ -keyout ${tmp_dir}/signing_key.pem 2>&1
+
+ openssl x509 -in ${tmp_dir}/signing_key.pem -out \
+ ${tmp_dir}/signing_key.der -outform der
+}
+
+mldsa_skip()
+{
+ local tmp_dir="$1"
+
+ rm -f ${tmp_dir}/x509.genkey ${tmp_dir}/signing_key.pem \
+ ${tmp_dir}/signing_key.der ${tmp_dir}/probe \
+ ${tmp_dir}/probe.p7s
+ exit 77
+}
+
+setup_mldsa()
+{
+ local tmp_dir="$1"
+
+ mldsa_supported "${tmp_dir}" || mldsa_skip "${tmp_dir}"
+ key_id=$(cat ${tmp_dir}/signing_key.der |
+ keyctl padd asymmetric ebpf_testing_key @s)
+ keyring_id=$(keyctl newring ebpf_testing_keyring @s)
+ keyctl link $key_id $keyring_id
+}
+
cleanup() {
local tmp_dir="$1"
@@ -91,7 +142,7 @@ catch()
local exit_code="$1"
local log_file="$2"
- if [[ "${exit_code}" -ne 0 ]]; then
+ if [[ "${exit_code}" -ne 0 && "${exit_code}" -ne 77 ]]; then
cat "${log_file}" >&3
fi
@@ -108,8 +159,10 @@ main()
[[ ! -d "${tmp_dir}" ]] && echo "Directory ${tmp_dir} doesn't exist" && exit 1
- if [[ "${action}" == "setup" ]]; then
- setup "${tmp_dir}"
+ if [[ "${action}" == "setup-rsa" ]]; then
+ setup_rsa "${tmp_dir}"
+ elif [[ "${action}" == "setup-mldsa" ]]; then
+ setup_mldsa "${tmp_dir}"
elif [[ "${action}" == "genkey" ]]; then
genkey "${tmp_dir}"
elif [[ "${action}" == "cleanup" ]]; then
diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh
index 6a3d026d76bd6..e7e0b419a0b87 100755
--- a/tools/testing/selftests/bpf/vmtest.sh
+++ b/tools/testing/selftests/bpf/vmtest.sh
@@ -107,6 +107,14 @@ Options:
-s) Instead of powering off the VM, start an interactive
shell. If <command> is specified, the shell runs after
the command finishes executing
+
+Environment variables:
+
+ KERNEL_CMDLINE_EXTRA
+ Extra arguments to append to the guest kernel command
+ line, for tests that need a boot-time setting. e.g:
+
+ KERNEL_CMDLINE_EXTRA="bpf.keyring_unsealed=1" $0 -- ./test_progs -t signed_loader
EOF
}
@@ -286,6 +294,12 @@ EOF
QEMU_FLAGS=("${HOST_FLAGS[@]}")
fi
+ local kernel_cmdline="root=/dev/vda rw console=${QEMU_CONSOLE}"
+
+ if [[ -n "${KERNEL_CMDLINE_EXTRA:-}" ]]; then
+ kernel_cmdline+=" ${KERNEL_CMDLINE_EXTRA}"
+ fi
+
${QEMU_BINARY} \
-nodefaults \
-display none \
@@ -294,7 +308,7 @@ EOF
-m 4G \
-drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
-kernel "${kernel_bzimage}" \
- -append "root=/dev/vda rw console=${QEMU_CONSOLE}"
+ -append "${kernel_cmdline}"
}
copy_logs()
|
| 3/2 | 2026/08/28 18:30 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 25813 output: 21 thoughts: 538 Thoughts: **Patch Analysis & Fuzzing Strategy** Okay, let's break down this patch and figure out the fuzzing strategy. My initial impression is that this is touching some fairly sensitive areas. I'm seeing changes across documentation, kernel headers, C code, and even userspace tools, so we've got a good mix of potential attack surfaces. First, I need to understand what's been changed. The modifications to documentation, like `kernel-parameters.txt` and `bpf/signing.rst`, are useful context but less directly relevant to fuzzing. I'll need to know what new parameters exist and what the signing implications are, but this likely just adds information, not code, so I'll put this on the back burner. The header files are more interesting. The introduction of `KEY_SPEC_BPF_KEYRING` in `uapi/linux/keyctl.h` immediately flags a new key type. The `include/linux/bpf.h` additions probably define new BPF-related structures or constants, which are always good targets for fuzzing. I'll need to check the exact new additions. The C code changes are where the real work lies. The `kernel/bpf/keys.c` file introducing `bpf_keyring` management looks like a prime target. We've got `bpf_keyring_unsealed`, `bpf_keyring_enforced()`, `bpf_lookup_keyring()`, `bpf_keyring_init()`. These new functions related to managing `bpf_keyring` are high-priority. We need to understand how they are called and what the possible data flows are. Any time we're dealing with keyring management and BPF, we need to be extra cautious. The `kernel/bpf/verifier.c` changes are also very important. Setting `BPF_PROG_MAX_SIGNATURE_SIZE` to 64K is significant, and I need to understand why this limit was chosen. The updates to `bpf_classify_keyring` and `bpf_prog_verify_signature` are critical. These functions are where validation and security checks are happening. Specifically, the change to `bpf_prog_verify_signature` is vital. The changed signature size check is a classic area for memory corruption bugs, and the addition of `bpf_lookup_keyring()` and the `bpf_keyring_enforced()` check indicate new logic around keyring validation and access control. These are top priorities for fuzzing. The modifications in `security/keys/process_keys.c` are also noteworthy. The addition of `key_register_bpf_keyring()` and `lookup_user_key()` handling of `KEY_SPEC_BPF_KEYRING` suggests a tight integration with the existing key management system. This could open up several paths to attack the kernel. Finally, the changes to the userspace tools and tests, `bpftool` and the selftests, are invaluable for figuring out how these new features are *meant* to be used. They should provide good starting points for understanding the intended behavior, the expected inputs, and how the new code interacts with the existing system. The test cases could also be a good start for building my own fuzzing harness. My next step is to perform some code searches, cross-reference these new symbols, and map their relationships. I want to build a call graph and identify the key entry points and any potential data flows or shared resources. Once I have a clear understanding of the patch's architecture, I can decide the best fuzzing configurations and choose the most effective focus symbols to target. This is all about finding the high-value targets, understanding the potential attack surface, and creating effective fuzzing harnesses. |
| 4/2 | 2026/08/28 18:31 | tool | grepper |
0mArgs:{
"Expression": "bpf_lookup_keyring"
}Results: {
"Output": "include/linux/bpf.h=3822=struct bpf_key *bpf_lookup_system_key(u64 id);\ninclude/linux/bpf.h:3823:struct bpf_key *bpf_lookup_keyring(void);\ninclude/linux/bpf.h-3824-bool bpf_keyring_enforced(void);\n--\ninclude/linux/bpf.h=3840=static inline struct bpf_key *bpf_lookup_system_key(u64 id)\n--\ninclude/linux/bpf.h-3844-\ninclude/linux/bpf.h:3845:static inline struct bpf_key *bpf_lookup_keyring(void)\ninclude/linux/bpf.h-3846-{\n--\nkernel/bpf/keys.c=21=bool bpf_keyring_enforced(void)\n--\nkernel/bpf/keys.c-25-\nkernel/bpf/keys.c:26:struct bpf_key *bpf_lookup_keyring(void)\nkernel/bpf/keys.c-27-{\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21029-\t} else if (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\nkernel/bpf/verifier.c:21030:\t\tkey = bpf_lookup_keyring();\nkernel/bpf/verifier.c-21031-\t} else if (bpf_keyring_enforced()) {\n"
} |
| 5/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 27610 output: 34 thoughts: 62 Thoughts: **Initial Investigation into BPF and Key Management** Okay, so I need to dig into this. My first instinct is to check a few key areas within the kernel. I'll start with `kernel/bpf/keys.c`, because anything to do with eBPF is always worth a look and key management seems relevant. I'll also dive into `security/keys/process_keys.c` since process keys are a core component of this. Finally, let's take a quick scan of `kernel/bpf/verifier.c` to see if that gives me any clues. Right, I think reading through `kernel/bpf/keys.c` and `security/keys/process_keys.c` will be the best starting point. Let's see what's actually going on in those files. |
| 6/2 | 2026/08/28 18:31 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/keys.c",
"FirstLine": 1,
"LineCount": 80
}Results: {
"Contents": " 1:\t// SPDX-License-Identifier: GPL-2.0-only\n 2:\t/* Copyright (c) 2026 Isovalent */\n 3:\t\n 4:\t#include \u003clinux/bpf.h\u003e\n 5:\t#include \u003clinux/cred.h\u003e\n 6:\t#include \u003clinux/err.h\u003e\n 7:\t#include \u003clinux/init.h\u003e\n 8:\t#include \u003clinux/key.h\u003e\n 9:\t#include \u003clinux/moduleparam.h\u003e\n 10:\t#include \u003clinux/slab.h\u003e\n 11:\t\n 12:\t#undef MODULE_PARAM_PREFIX\n 13:\t#define MODULE_PARAM_PREFIX \"bpf.\"\n 14:\t\n 15:\tstatic struct key *bpf_keyring;\n 16:\t\n 17:\tstatic bool bpf_keyring_unsealed __ro_after_init;\n 18:\tmodule_param_named(keyring_unsealed, bpf_keyring_unsealed, bool, 0444);\n 19:\tMODULE_PARM_DESC(keyring_unsealed, \"Leave the bpf keyring unsealed\");\n 20:\t\n 21:\tbool bpf_keyring_enforced(void)\n 22:\t{\n 23:\t\treturn bpf_keyring_unsealed;\n 24:\t}\n 25:\t\n 26:\tstruct bpf_key *bpf_lookup_keyring(void)\n 27:\t{\n 28:\t\tstruct bpf_key *bkey;\n 29:\t\n 30:\t\tif (!bpf_keyring)\n 31:\t\t\treturn NULL;\n 32:\t\tif (!READ_ONCE(bpf_keyring-\u003ekeys.nr_leaves_on_tree) ||\n 33:\t\t !READ_ONCE(bpf_keyring-\u003erestrict_link))\n 34:\t\t\treturn NULL;\n 35:\t\n 36:\t\tbkey = kmalloc_obj(*bkey);\n 37:\t\tif (!bkey)\n 38:\t\t\treturn NULL;\n 39:\t\n 40:\t\tbkey-\u003ekey = bpf_keyring;\n 41:\t\tbkey-\u003ehas_ref = false;\n 42:\t\treturn bkey;\n 43:\t}\n 44:\t\n 45:\tstatic int __init bpf_keyring_init(void)\n 46:\t{\n 47:\t\tstruct key *keyring;\n 48:\t\n 49:\t\tkeyring = keyring_alloc(\".bpf\",\n 50:\t\t\t\t\tGLOBAL_ROOT_UID, GLOBAL_ROOT_GID,\n 51:\t\t\t\t\tcurrent_cred(), KEY_POS_SEARCH |\n 52:\t\t\t\t\tKEY_USR_VIEW | KEY_USR_READ |\n 53:\t\t\t\t\tKEY_USR_WRITE | KEY_USR_SEARCH |\n 54:\t\t\t\t\tKEY_USR_SETATTR, KEY_ALLOC_NOT_IN_QUOTA,\n 55:\t\t\t\t\tNULL, NULL);\n 56:\t\tif (IS_ERR(keyring)) {\n 57:\t\t\tpr_err(\"bpf: cannot allocate bpf keyring: %ld\\n\",\n 58:\t\t\t PTR_ERR(keyring));\n 59:\t\t\treturn 0;\n 60:\t\t}\n 61:\t\tif (!bpf_keyring_unsealed \u0026\u0026\n 62:\t\t keyring_restrict(make_key_ref(keyring, true), NULL, NULL)) {\n 63:\t\t\tpr_err(\"bpf: cannot seal bpf keyring\\n\");\n 64:\t\t\tkey_revoke(keyring);\n 65:\t\t\tkey_put(keyring);\n 66:\t\t\treturn 0;\n 67:\t\t}\n 68:\t\n 69:\t\tbpf_keyring = keyring;\n 70:\t\tkey_register_bpf_keyring(keyring);\n 71:\t\treturn 0;\n 72:\t}\n 73:\tlate_initcall(bpf_keyring_init);\n"
} |
| 7/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28906 output: 37 thoughts: 37 Thoughts: Okay, here's my interpretation of those thoughts, framed as my own: **Digging into Key Management in the Kernel** Alright, I need to understand how these keys are being managed, especially within the context of BPF. Specifically, I'm thinking I should really focus in on the `security/keys/process_keys.c` file. I'm going to start with the `key_register_bpf_keyring` function – that feels like a crucial entry point for BPF interacting with the key subsystem. Understanding its logic will give me a good handle on how BPF keys are being created and handled from the ground up. Then, while I'm in that area, I'll take a look at `lookup_user_key`, since it's likely to be involved in the security checks and permissions. I'm expecting that if I understand how the lookup works, I'll see some obvious things around the security decisions being made about keys. I'm keen to get a solid grasp of how these functions relate to each other in terms of BPF key management. |
| 8/2 | 2026/08/28 18:31 | tool | read-file |
0mArgs:{
"File": "security/keys/process_keys.c",
"FirstLine": 740,
"LineCount": 40
}Results: {
"Contents": " 740:\t\n 741:\t\tcase KEY_SPEC_REQUESTOR_KEYRING:\n 742:\t\t\tif (!ctx.cred-\u003erequest_key_auth)\n 743:\t\t\t\tgoto error;\n 744:\t\n 745:\t\t\tdown_read(\u0026ctx.cred-\u003erequest_key_auth-\u003esem);\n 746:\t\t\tif (test_bit(KEY_FLAG_REVOKED,\n 747:\t\t\t\t \u0026ctx.cred-\u003erequest_key_auth-\u003eflags)) {\n 748:\t\t\t\tkey_ref = ERR_PTR(-EKEYREVOKED);\n 749:\t\t\t\tkey = NULL;\n 750:\t\t\t} else {\n 751:\t\t\t\trka = ctx.cred-\u003erequest_key_auth-\u003epayload.data[0];\n 752:\t\t\t\tkey = rka-\u003edest_keyring;\n 753:\t\t\t\t__key_get(key);\n 754:\t\t\t}\n 755:\t\t\tup_read(\u0026ctx.cred-\u003erequest_key_auth-\u003esem);\n 756:\t\t\tif (!key)\n 757:\t\t\t\tgoto error;\n 758:\t\t\tkey_ref = make_key_ref(key, 1);\n 759:\t\t\tbreak;\n 760:\t\n 761:\t\tcase KEY_SPEC_BPF_KEYRING:\n 762:\t\t\tkey = bpf_keyring;\n 763:\t\t\tif (!key)\n 764:\t\t\t\tgoto error;\n 765:\t\t\t__key_get(key);\n 766:\t\t\tkey_ref = make_key_ref(key, 0);\n 767:\t\t\tbreak;\n 768:\t\n 769:\t\tdefault:\n 770:\t\t\tkey_ref = ERR_PTR(-EINVAL);\n 771:\t\t\tif (id \u003c 1)\n 772:\t\t\t\tgoto error;\n 773:\t\n 774:\t\t\tkey = key_lookup(id);\n 775:\t\t\tif (IS_ERR(key)) {\n 776:\t\t\t\tkey_ref = ERR_CAST(key);\n 777:\t\t\t\tgoto error;\n 778:\t\t\t}\n 779:\t\n"
} |
| 9/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 29776 output: 31 thoughts: 536 Thoughts: **Fuzzing Target Identification: `lookup_user_key` & Related Kernel Logic** Okay, so I've been looking at `lookup_user_key()`. It's definitely worth a closer look for fuzzing potential. Specifically, I see it's now reachable from the userspace keyctl, add_key, and request_key syscalls, thanks to the new `KEY_SPEC_BPF_KEYRING` specifier. Also, the BPF syscall (`BPF_PROG_LOAD`) with signature verification utilizes `bpf_prog_verify_signature()`, which in turn calls `bpf_lookup_keyring()`, `bpf_keyring_enforced()`, and yes, `bpf_lookup_user_key()`. The signature verification path within `kernel/bpf/verifier.c` is the key here when loading a BPF program using `attr->signature` and `attr->signature_size`. Userspace can already interact with the `.bpf` keyring using `add_key` and `keyctl` (e.g., `KEYCTL_RESTRICT_KEYRING`, `KEYCTL_GET_KEYRING_ID`, `KEYCTL_SETPERM`, etc.) by specifying `KEY_SPEC_BPF_KEYRING`. This is a juicy target because it's modifying core kernel logic: the BPF and Keys subsystems. Specifically: * It introduces `KEY_SPEC_BPF_KEYRING` in `lookup_user_key` within `security/keys/process_keys.c`. * A whole new file, `kernel/bpf/keys.c`, has been added with functions like `bpf_lookup_keyring`, `bpf_keyring_enforced`, `bpf_keyring_init`, and `key_register_bpf_keyring`. * Significant changes are happening in BPF program loading and signature verification within `kernel/bpf/verifier.c`, including `bpf_prog_verify_signature` and `bpf_classify_keyring`. * Plus signature size limits are changing, and enforcement logic as well. Now, before I get too deep, I need to check the Kconfig options. I need to figure out which options control the relevant code: `kernel/bpf/keys.c`, `kernel/bpf/verifier.c`, and `security/keys/process_keys.c`. Let's start with `kernel/bpf/Makefile` and see what it reveals about `CONFIG_KEYS` and `CONFIG_BPF_SYSCALL`. I'll grep the Kconfig files next to understand the dependencies on those. |
| 10/2 | 2026/08/28 18:31 | tool | grepper |
0mError:no matches Args: {
"Expression": "config BPF_SYSCALL",
"PathPrefix": "init/Kconfig"
}Results: {
"error": "no matches"
} |
| 11/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 30934 output: 21 thoughts: 20 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Investigating `BPF_SYSCALL` and `KEYS` Definitions** Alright, so the first step in understanding this situation is to pinpoint the exact locations where `BPF_SYSCALL` and `KEYS` are defined. These macros or enum values are critical, and I need to see their context. Knowing their definitions tells me everything from the potential underlying data structures involved to what values are permitted in certain operations. Without this, I'm essentially operating in the dark. It would be difficult to formulate any useful actions or draw meaningful conclusions about the system. I'm going to start by running a quick search, probably using something like `grep` or `find` across the relevant kernel source trees and include paths. I expect these identifiers likely to be found in headers related to BPF or system call handling, potentially even within definitions tied to security modules or key management subsystems, depending on the kernel version and configuration. Knowing the file locations, I can then dive into the relevant header files, and, once I understand the type of data they are defined as, I can get a better idea of how they are used within the kernel. |
| 12/2 | 2026/08/28 18:31 | tool | grepper |
0mArgs:{
"Expression": "config BPF_SYSCALL"
}Results: {
"Output": "kernel/bpf/Kconfig=25=menu \"BPF subsystem\"\nkernel/bpf/Kconfig-26-\nkernel/bpf/Kconfig:27:config BPF_SYSCALL\nkernel/bpf/Kconfig-28-\tbool \"Enable bpf() system call\"\n"
} |
| 13/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 31311 output: 34 thoughts: 19 Thoughts: Here's my summary, as if I were thinking these thoughts: **Examining Kernel BPF Configuration** Okay, let's dive into this. To get a handle on this, I'm going to navigate to the `kernel/bpf/Kconfig` file. I specifically want to zero in around line 25. This is likely where the core configuration options related to BPF are defined. I'm expecting to find the top-level switches that control the inclusion or exclusion of the BPF subsystem, and potentially some general feature flags. Line 25 should give me a good entry point to understand how BPF is exposed to kernel config. Let's see what's there. |
| 14/2 | 2026/08/28 18:31 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/Kconfig",
"FirstLine": 25,
"LineCount": 50
}Results: {
"Contents": " 25:\tmenu \"BPF subsystem\"\n 26:\t\n 27:\tconfig BPF_SYSCALL\n 28:\t\tbool \"Enable bpf() system call\"\n 29:\t\tselect BPF\n 30:\t\tselect IRQ_WORK\n 31:\t\tselect NEED_TASKS_RCU\n 32:\t\tselect TASKS_TRACE_RCU\n 33:\t\tselect BINARY_PRINTF\n 34:\t\tselect NET_SOCK_MSG if NET\n 35:\t\tselect NET_XGRESS if NET\n 36:\t\tselect PAGE_POOL if NET\n 37:\t\tdefault n\n 38:\t\thelp\n 39:\t\t Enable the bpf() system call that allows to manipulate BPF programs\n 40:\t\t and maps via file descriptors.\n 41:\t\n 42:\tconfig BPF_JIT\n 43:\t\tbool \"Enable BPF Just In Time compiler\"\n 44:\t\tdepends on BPF\n 45:\t\tdepends on HAVE_CBPF_JIT || HAVE_EBPF_JIT\n 46:\t\tselect EXECMEM\n 47:\t\thelp\n 48:\t\t BPF programs are normally handled by a BPF interpreter. This option\n 49:\t\t allows the kernel to generate native code when a program is loaded\n 50:\t\t into the kernel. This will significantly speed-up processing of BPF\n 51:\t\t programs.\n 52:\t\n 53:\t\t Note, an admin should enable this feature changing:\n 54:\t\t /proc/sys/net/core/bpf_jit_enable\n 55:\t\t /proc/sys/net/core/bpf_jit_harden (optional)\n 56:\t\t /proc/sys/net/core/bpf_jit_kallsyms (optional)\n 57:\t\n 58:\tconfig BPF_JIT_ALWAYS_ON\n 59:\t\tbool \"Permanently enable BPF JIT and remove BPF interpreter\"\n 60:\t\tdepends on BPF_SYSCALL \u0026\u0026 HAVE_EBPF_JIT \u0026\u0026 BPF_JIT\n 61:\t\thelp\n 62:\t\t Enables BPF JIT and removes BPF interpreter to avoid speculative\n 63:\t\t execution of BPF instructions by the interpreter.\n 64:\t\n 65:\t\t When CONFIG_BPF_JIT_ALWAYS_ON is enabled, /proc/sys/net/core/bpf_jit_enable\n 66:\t\t is permanently set to 1 and setting any other value than that will\n 67:\t\t return failure.\n 68:\t\n 69:\tconfig BPF_JIT_DEFAULT_ON\n 70:\t\tdef_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON\n 71:\t\tdepends on HAVE_EBPF_JIT \u0026\u0026 BPF_JIT\n 72:\t\n 73:\tconfig BPF_UNPRIV_DEFAULT_OFF\n 74:\t\tbool \"Disable unprivileged BPF by default\"\n"
} |
| 15/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 32277 output: 18 thoughts: 12 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Hunting Down Configuration Keys in Kconfig** Okay, so I need to track down where certain configuration keys, specifically the ones related to a `config` option, are defined within the Kconfig system. The immediate instinct is to just dive into the files and start grepping, but I need to be a bit more methodical. Let's start with the most obvious and likely approach. I want to search for the specific instances where a `config` key is declared. Hopefully, this will lead me to the right file and the surrounding context, so I can see how it’s being used and any dependencies it might have. Understanding the definition is key to tracking how this is being used across my configuration tree. |
| 16/2 | 2026/08/28 18:31 | tool | grepper |
0mArgs:{
"Expression": "config KEYS\\b"
}Results: {
"Output": "security/keys/Kconfig-5-\nsecurity/keys/Kconfig:6:menuconfig KEYS\nsecurity/keys/Kconfig-7-\tbool \"Enable access key retention support\"\n"
} |
| 17/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 32525 output: 32 thoughts: 17 Thoughts: Here's my take on the thought process, presented as a first-person summary, tailored for someone familiar with embedded systems configuration: **Diving into Kconfig: Security Keys** Okay, time to dig into the security aspects of the kernel configuration. First thing, I'm heading over to the `security/keys/Kconfig` file. I'm focusing specifically around line 1, which typically marks the beginning of the relevant section in a Kconfig file. This usually means it's the entry point or the general overview of the security key configuration options. I'm expecting to see the main `config` block that introduces the concept of security keys, perhaps defining a top-level symbol or feature flag. It might establish dependencies, default values, and a broad description of what the security keys subsystem provides. Knowing the general layout of Kconfig files, I want to quickly grasp the high-level structure to understand how to best navigate the different options later. This starting point will help me frame my understanding of the security key configuration and the breadth of possibilities within the kernel. I will have a more informed position with regards to what can be enabled or disabled, and how it effects performance and security. |
| 18/2 | 2026/08/28 18:31 | tool | read-file |
0mArgs:{
"File": "security/keys/Kconfig",
"FirstLine": 1,
"LineCount": 60
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0-only\n 2:\t#\n 3:\t# Key management configuration\n 4:\t#\n 5:\t\n 6:\tmenuconfig KEYS\n 7:\t\tbool \"Enable access key retention support\"\n 8:\t\tselect ASSOCIATIVE_ARRAY\n 9:\t\thelp\n 10:\t\t This option provides support for retaining authentication tokens and\n 11:\t\t access keys in the kernel.\n 12:\t\n 13:\t\t It also includes provision of methods by which such keys might be\n 14:\t\t associated with a process so that network filesystems, encryption\n 15:\t\t support and the like can find them.\n 16:\t\n 17:\t\t Furthermore, a special type of key is available that acts as keyring:\n 18:\t\t a searchable sequence of keys. Each process is equipped with access\n 19:\t\t to five standard keyrings: UID-specific, GID-specific, session,\n 20:\t\t process and thread.\n 21:\t\n 22:\t\t If you are unsure as to whether this is required, answer N.\n 23:\t\n 24:\tif KEYS\n 25:\t\n 26:\tconfig KEYS_REQUEST_CACHE\n 27:\t\tbool \"Enable temporary caching of the last request_key() result\"\n 28:\t\thelp\n 29:\t\t This option causes the result of the last successful request_key()\n 30:\t\t call that didn't upcall to the kernel to be cached temporarily in the\n 31:\t\t task_struct. The cache is cleared by exit and just prior to the\n 32:\t\t resumption of userspace.\n 33:\t\n 34:\t\t This allows the key used for multiple step processes where each step\n 35:\t\t wants to request a key that is likely the same as the one requested\n 36:\t\t by the last step to save on the searching.\n 37:\t\n 38:\t\t An example of such a process is a pathwalk through a network\n 39:\t\t filesystem in which each method needs to request an authentication\n 40:\t\t key. Pathwalk will call multiple methods for each dentry traversed\n 41:\t\t (permission, d_revalidate, lookup, getxattr, getacl, ...).\n 42:\t\n 43:\tconfig PERSISTENT_KEYRINGS\n 44:\t\tbool \"Enable register of persistent per-UID keyrings\"\n 45:\t\thelp\n 46:\t\t This option provides a register of persistent per-UID keyrings,\n 47:\t\t primarily aimed at Kerberos key storage. The keyrings are persistent\n 48:\t\t in the sense that they stay around after all processes of that UID\n 49:\t\t have exited, not that they survive the machine being rebooted.\n 50:\t\n 51:\t\t A particular keyring may be accessed by either the user whose keyring\n 52:\t\t it is or by a process with administrative privileges. The active\n 53:\t\t LSMs gets to rule on which admin-level processes get to access the\n 54:\t\t cache.\n 55:\t\n 56:\t\t Keyrings are created and added into the register upon demand and get\n 57:\t\t removed if they expire (a default timeout is set upon creation).\n 58:\t\n 59:\tconfig BIG_KEYS\n 60:\t\tbool \"Large payload keys\"\n"
} |
| 19/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 33682 output: 22 thoughts: 42 Thoughts: **Digging Deeper into Asymmetric Keys and Signatures** Okay, so we've covered the basics, but I need to make sure we haven't missed anything crucial regarding asymmetric key types and signature verification configurations, especially if they're interacting with BPF. My usual approach is to be thorough. Let's see... I'll run a quick `grep` through the code, searching for `ASYMMETRIC_KEY_TYPE` or any settings related to signature verification used within the BPF context. That should quickly uncover any relevant configurations that we need to examine further. I want to make certain that we haven't overlooked any details with those. It is crucial to be as complete as possible. |
| 20/2 | 2026/08/28 18:31 | tool | grepper |
0mArgs:{
"Expression": "ASYMMETRIC_KEY_TYPE"
}Results: {
"Output": "arch/arm/configs/wpcm450_defconfig=185=CONFIG_CRYPTO_SHA256=y\narch/arm/configs/wpcm450_defconfig:186:CONFIG_ASYMMETRIC_KEY_TYPE=y\narch/arm/configs/wpcm450_defconfig-187-CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y\n--\ncerts/Kconfig=77=config SYSTEM_TRUSTED_KEYRING\n--\ncerts/Kconfig-79-\tdepends on KEYS\ncerts/Kconfig:80:\tdepends on ASYMMETRIC_KEY_TYPE\ncerts/Kconfig-81-\tdepends on X509_CERTIFICATE_PARSER = y\n--\ncrypto/Makefile=197=obj-$(CONFIG_ASYNC_CORE) += async_tx/\ncrypto/Makefile:198:obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys/\ncrypto/Makefile-199-crypto_simd-y := simd.o\n--\ncrypto/asymmetric_keys/Kconfig-1-# SPDX-License-Identifier: GPL-2.0\ncrypto/asymmetric_keys/Kconfig:2:menuconfig ASYMMETRIC_KEY_TYPE\ncrypto/asymmetric_keys/Kconfig-3-\tbool \"Asymmetric (public-key cryptographic) key type\"\n--\ncrypto/asymmetric_keys/Kconfig-10-\ncrypto/asymmetric_keys/Kconfig:11:if ASYMMETRIC_KEY_TYPE\ncrypto/asymmetric_keys/Kconfig-12-\n--\ncrypto/asymmetric_keys/Kconfig=110=config FIPS_SIGNATURE_SELFTEST_ECDSA\n--\ncrypto/asymmetric_keys/Kconfig-116-\ncrypto/asymmetric_keys/Kconfig:117:endif # ASYMMETRIC_KEY_TYPE\n--\ncrypto/asymmetric_keys/Makefile-5-\ncrypto/asymmetric_keys/Makefile:6:obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o\ncrypto/asymmetric_keys/Makefile-7-\n--\ndrivers/firmware/Kconfig=296=config TURRIS_MOX_RWTM_KEYCTL\n--\ndrivers/firmware/Kconfig-299-\tdepends on KEYS\ndrivers/firmware/Kconfig:300:\tdepends on ASYMMETRIC_KEY_TYPE\ndrivers/firmware/Kconfig-301-\tselect CZNIC_PLATFORMS\n--\ndrivers/platform/cznic/Kconfig=79=config TURRIS_OMNIA_MCU_KEYCTL\n--\ndrivers/platform/cznic/Kconfig-82-\tdepends on KEYS\ndrivers/platform/cznic/Kconfig:83:\tdepends on ASYMMETRIC_KEY_TYPE\ndrivers/platform/cznic/Kconfig-84-\tdepends on TURRIS_OMNIA_MCU_GPIO\n--\ndrivers/platform/cznic/Kconfig=93=config TURRIS_SIGNING_KEY\n--\ndrivers/platform/cznic/Kconfig-95-\tdepends on KEYS\ndrivers/platform/cznic/Kconfig:96:\tdepends on ASYMMETRIC_KEY_TYPE\ndrivers/platform/cznic/Kconfig-97-\n--\ninclude/crypto/public_key.h=74=extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,\n--\ninclude/crypto/public_key.h-78-\ninclude/crypto/public_key.h:79:#if IS_REACHABLE(CONFIG_ASYMMETRIC_KEY_TYPE)\ninclude/crypto/public_key.h-80-extern int restrict_link_by_ca(struct key *dest_keyring,\n--\ninit/Kconfig=2187=config SYSTEM_DATA_VERIFICATION\n--\ninit/Kconfig-2192-\tselect CRYPTO_RSA\ninit/Kconfig:2193:\tselect ASYMMETRIC_KEY_TYPE\ninit/Kconfig-2194-\tselect ASYMMETRIC_PUBLIC_KEY_SUBTYPE\n--\nsecurity/integrity/Kconfig=33=config INTEGRITY_ASYMMETRIC_KEYS\n--\nsecurity/integrity/Kconfig-36-\tdefault n\nsecurity/integrity/Kconfig:37:\tselect ASYMMETRIC_KEY_TYPE\nsecurity/integrity/Kconfig-38-\tselect ASYMMETRIC_PUBLIC_KEY_SUBTYPE\n--\nsecurity/keys/Makefile=24=obj-$(CONFIG_KEY_DH_OPERATIONS) += dh.o\nsecurity/keys/Makefile:25:obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += keyctl_pkey.o\nsecurity/keys/Makefile-26-\n--\nsecurity/keys/internal.h=301=static inline long compat_keyctl_dh_compute(\n--\nsecurity/keys/internal.h-310-\nsecurity/keys/internal.h:311:#ifdef CONFIG_ASYMMETRIC_KEY_TYPE\nsecurity/keys/internal.h-312-extern long keyctl_pkey_query(key_serial_t,\n--\nsecurity/keys/keyctl.c=29=static const unsigned char keyrings_capabilities[2] = {\n--\nsecurity/keys/keyctl.c-32-\t (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS)\t? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) |\nsecurity/keys/keyctl.c:33:\t (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE)\t? KEYCTL_CAPS0_PUBLIC_KEY : 0) |\nsecurity/keys/keyctl.c-34-\t (IS_ENABLED(CONFIG_BIG_KEYS)\t\t? KEYCTL_CAPS0_BIG_KEY : 0) |\n--\ntools/testing/selftests/dm-verity/config=5=CONFIG_KEYS=y\ntools/testing/selftests/dm-verity/config:6:CONFIG_ASYMMETRIC_KEY_TYPE=y\ntools/testing/selftests/dm-verity/config-7-CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y\n"
} |
| 21/2 | 2026/08/28 18:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 35408 output: 22 thoughts: 39 Thoughts: Okay, let's dive into this. Here's how I'd approach summarizing those thoughts, given my expertise: **Examining BPF Signature Verification Configuration** Right, so I'm thinking, "Okay, time to get a handle on how BPF handles signature verification. I need to understand this mechanism." My initial strategy? Let's get straight to the code. Specifically, I'm thinking I'll start by grepping the kernel source. I'll search for key functions and system calls that are likely involved: `bpf_prog_verify_signature` seems like a pretty direct lead, that should get me on the right track; and, `bpf_lookup_user_key` could be another important function to look at, probably related to a key verification mechanism. I'm expecting those key search terms to yield insights into the overall signature verification implementation within the BPF framework. That should give me a good jumping-off point to build a mental map of the system. |
| 22/2 | 2026/08/28 18:32 | tool | grepper |
0mArgs:{
"Expression": "bpf_prog_verify_signature"
}Results: {
"Output": "kernel/bpf/verifier.c=20984=static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)\n--\nkernel/bpf/verifier.c-21008- */\nkernel/bpf/verifier.c:21009:static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-21010-\t\t\t\t union bpf_attr *attr, bool is_kernel)\n--\nkernel/bpf/verifier.c=21133=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-21184-\tif (env-\u003esignature) {\nkernel/bpf/verifier.c:21185:\t\tret = bpf_prog_verify_signature(env, attr, uattr.is_kernel);\nkernel/bpf/verifier.c-21186-\t\tif (ret)\n--\ntools/testing/selftests/bpf/prog_tests/signed_loader.c=641=static void signature_bad_keyring(void)\n--\ntools/testing/selftests/bpf/prog_tests/signed_loader.c-649-\t\t * A present signature with a keyring_id that resolves to no key is\ntools/testing/selftests/bpf/prog_tests/signed_loader.c:650:\t\t * rejected up front: bpf_prog_verify_signature() fails the keyring\ntools/testing/selftests/bpf/prog_tests/signed_loader.c-651-\t\t * lookup (-EINVAL) before it ever looks at the signature bytes. A\n"
} |
| 23/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 36122 output: 38 thoughts: 103 Thoughts: **Examining BPF Self-Test Configurations and Verifier Code** Okay, let's dive in. I need to understand the implications of these recent changes. My first step is to quickly examine the configurations within `tools/testing/selftests/bpf/config`, specifically focusing on those related to cryptographic primitives and key management. The diff showed these new additions: `CONFIG_CRYPTO_MLDSA=y`, `CONFIG_CRYPTO_SHA256=y`, and `CONFIG_CRYPTO_SHA512=y`. This strongly suggests an interaction with cryptographic operations, possibly related to signature verification or integrity checks within the BPF context. Now, I need to check `kernel/bpf/verifier.c`, especially around the `bpf_prog_verify_signature` function. This area is crucial, as that's where the kernel's BPF verifier handles signature checks. I'll need to understand how these new crypto configurations are being utilized within the verification process itself. My guess is we'll find some new dependencies or changes related to these new cryptographic features within the BPF verification stage. |
| 24/2 | 2026/08/28 18:32 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 21010,
"LineCount": 60
}Results: {
"Contents": "21010:\t\t\t\t\t union bpf_attr *attr, bool is_kernel)\n21011:\t{\n21012:\t\tbpfptr_t usig = make_bpfptr(attr-\u003esignature, is_kernel);\n21013:\t\tstruct bpf_dynptr_kern sig_ptr, data_ptr;\n21014:\t\tstruct bpf_prog *prog = env-\u003eprog;\n21015:\t\tstruct bpf_map **maps = env-\u003eused_maps;\n21016:\t\tstruct bpf_key *key = NULL;\n21017:\t\tvoid *sig, *data = NULL;\n21018:\t\tu32 map_cnt = env-\u003eused_map_cnt;\n21019:\t\tu32 i, off, insns_sz;\n21020:\t\tu64 data_sz;\n21021:\t\tint err = 0;\n21022:\t\n21023:\t\tif (!attr-\u003esignature_size ||\n21024:\t\t attr-\u003esignature_size \u003e BPF_PROG_MAX_SIGNATURE_SIZE)\n21025:\t\t\treturn -EINVAL;\n21026:\t\n21027:\t\tif (system_keyring_id_check(attr-\u003ekeyring_id) == 0) {\n21028:\t\t\tkey = bpf_lookup_system_key(attr-\u003ekeyring_id);\n21029:\t\t} else if (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n21030:\t\t\tkey = bpf_lookup_keyring();\n21031:\t\t} else if (bpf_keyring_enforced()) {\n21032:\t\t\tverbose(env, \"caller-supplied keyring refused, use bpf keyring\\n\");\n21033:\t\t\treturn -EPERM;\n21034:\t\t} else {\n21035:\t\t\tkey = bpf_lookup_user_key(attr-\u003ekeyring_id, 0);\n21036:\t\t}\n21037:\t\tif (!key) {\n21038:\t\t\tif (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n21039:\t\t\t\tverbose(env, \"the bpf keyring is empty or has not been restricted\\n\");\n21040:\t\t\t\treturn -ENOKEY;\n21041:\t\t\t}\n21042:\t\t\tverbose(env, \"cannot resolve signing keyring with keyring_id %d\\n\",\n21043:\t\t\t\tattr-\u003ekeyring_id);\n21044:\t\t\treturn -EINVAL;\n21045:\t\t}\n21046:\t\n21047:\t\tsig = kvmemdup_bpfptr(usig, attr-\u003esignature_size);\n21048:\t\tif (IS_ERR(sig)) {\n21049:\t\t\tbpf_key_put(key);\n21050:\t\t\treturn PTR_ERR(sig);\n21051:\t\t}\n21052:\t\n21053:\t\tinsns_sz = prog-\u003elen * sizeof(struct bpf_insn);\n21054:\t\tdata_sz = insns_sz;\n21055:\t\tfor (i = 0; i \u003c map_cnt; i++) {\n21056:\t\t\tstruct bpf_map *map = maps[i];\n21057:\t\n21058:\t\t\tif (map-\u003emap_type != BPF_MAP_TYPE_ARRAY ||\n21059:\t\t\t !map-\u003eops-\u003emap_direct_value_addr) {\n21060:\t\t\t\tverbose(env, \"signed program metadata map '%s' must be an array\\n\",\n21061:\t\t\t\t\tmap-\u003ename);\n21062:\t\t\t\terr = -EINVAL;\n21063:\t\t\t\tgoto out;\n21064:\t\t\t}\n21065:\t\t\tif (!READ_ONCE(map-\u003efrozen)) {\n21066:\t\t\t\tverbose(env, \"signed program metadata map '%s' must be frozen\\n\",\n21067:\t\t\t\t\tmap-\u003ename);\n21068:\t\t\t\terr = -EPERM;\n21069:\t\t\t\tgoto out;\n"
} |
| 25/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 37591 output: 26 thoughts: 248 Thoughts: **My Analysis of Potential Kernel BPF Interaction Points** Okay, let's dive into these code changes. My goal here is to identify any non-static functions or key symbols that could potentially impact or interact with the BPF subsystem. I want to see how these changes are designed, what new functionality is enabled, or if they affect existing security structures. First, I've got to look at `kernel/bpf/keys.c`. Interesting. I see `bpf_keyring_enforced` and `bpf_lookup_keyring`. Non-static, which is what I'm looking for. They seem like prime candidates to understand how BPF interacts with the keyring mechanism. Also, there's `bpf_keyring_init`, which is called by `initcall`, so it's setting up the environment. Next up, `security/keys/process_keys.c`. Ah, here we have `key_register_bpf_keyring`, another non-static function called by an `initcall`. This is important, as it suggests the system is being configured at boot to accommodate BPF keys. I also see `lookup_user_key`, a non-static entrypoint in the keys subsystem. This is a very interesting piece - I need to confirm whether `lookup_user_key` is indeed non-static. This likely means it's accessible and possibly exposed to the BPF. Finally, `kernel/bpf/verifier.c`. `bpf_check` is non-static, and it calls `bpf_prog_verify_signature`, which is probably key to understanding how BPF programs are validated. Then there's `bpf_prog_load` in `kernel/bpf/syscall.c`, which is the entrypoint that triggers the verifier. Now I know the flow of execution and the main functions. Alright, time to go deeper and check that `lookup_user_key` function in `security/keys/process_keys.c`. Let's see if it's really non-static as I suspect, and if it is, figure out the interaction. |
| 26/2 | 2026/08/28 18:32 | tool | grepper |
0mArgs:{
"Expression": "key_ref_t lookup_user_key\\b"
}Results: {
"Output": "include/linux/key.h=452=extern void key_set_timeout(struct key *, unsigned);\ninclude/linux/key.h-453-\ninclude/linux/key.h:454:extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,\ninclude/linux/key.h-455-\t\t\t\t enum key_need_perm need_perm);\n--\nsecurity/keys/process_keys.c=605=void key_register_bpf_keyring(struct key *keyring)\n--\nsecurity/keys/process_keys.c-627- */\nsecurity/keys/process_keys.c:628:key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,\nsecurity/keys/process_keys.c-629-\t\t\t enum key_need_perm need_perm)\n"
} |
| 27/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 38549 output: 19 thoughts: 120 Thoughts: **Analyzing Potential Focus Symbols in the BPF Subsystem** Okay, let's get down to brass tacks. I need to pinpoint the key non-static functions within the BPF subsystem that might serve as effective "FocusSymbols" for my analysis. I'm thinking, off the top of my head, `bpf_lookup_keyring`, `bpf_keyring_enforced`, and `lookup_user_key` seem like solid candidates. I also need to consider the BPF program loading pathway. While `bpf_prog_load` is static, I should probably investigate `bpf_prog_load` or the underlying `__sys_bpf` function as potential entry points. To get a clearer picture of how these syscalls are handled, I need to consult the source code. Specifically, I should check `kernel/bpf/syscall.c` to see the syscall entry points and how these functions interact with the kernel. That will give me a better understanding of the overall flow and where my attention should be focused. |
| 28/2 | 2026/08/28 18:32 | tool | grepper |
0mArgs:{
"Expression": "bpf_check\\("
}Results: {
"Output": "Documentation/bpf/signing.rst=128=reuses the existing ``fd_array``:\n--\nDocumentation/bpf/signing.rst-134- set so the kernel knows the array's length.\nDocumentation/bpf/signing.rst:135:#. Signature verification runs inside the verifier (``bpf_check()``), once it\nDocumentation/bpf/signing.rst-136- has resolved the ``fd_array`` entries into the program's ``used_maps``. The\n--\nDocumentation/bpf/signing.rst=230=This hook gates admission **for every load**, from a single call site inside the\nDocumentation/bpf/signing.rst:231:verifier (``bpf_check()``), before the main verification work. It runs after the\nDocumentation/bpf/signing.rst-232-optional signature verification, so the verdict and keyring fields are final - the\n--\ninclude/linux/bpf.h=3052=struct bpf_log_attr;\ninclude/linux/bpf.h:3053:int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,\ninclude/linux/bpf.h-3054-\t struct bpf_log_attr *attr_log);\n--\ninclude/linux/bpf_verifier.h=907=struct bpf_fd_array {\n--\ninclude/linux/bpf_verifier.h-915-/* single container for all structs\ninclude/linux/bpf_verifier.h:916: * one verifier_env per bpf_check() call\ninclude/linux/bpf_verifier.h-917- */\n--\ninclude/linux/bpf_verifier.h=1709=int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 btf_id,\n--\ninclude/linux/bpf_verifier.h-1711-\ninclude/linux/bpf_verifier.h:1712:/* Functions in fixups.c, called from bpf_check() */\ninclude/linux/bpf_verifier.h-1713-int bpf_remove_fastcall_spills_fills(struct bpf_verifier_env *env);\n--\ninclude/net/tcp.h=717=static inline bool cookie_bpf_ok(struct sk_buff *skb)\n--\ninclude/net/tcp.h-721-\ninclude/net/tcp.h:722:struct request_sock *cookie_bpf_check(struct sock *sk, struct sk_buff *skb);\ninclude/net/tcp.h-723-#else\ninclude/net/tcp.h=724=static inline bool cookie_bpf_ok(struct sk_buff *skb)\n--\ninclude/net/tcp.h-728-\ninclude/net/tcp.h:729:static inline struct request_sock *cookie_bpf_check(struct net *net, struct sock *sk,\ninclude/net/tcp.h-730-\t\t\t\t\t\t struct sk_buff *skb)\n--\nkernel/bpf/syscall.c=2917=static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\n--\nkernel/bpf/syscall.c-3132-\t/* run eBPF verifier */\nkernel/bpf/syscall.c:3133:\terr = bpf_check(\u0026prog, attr, uattr, attr_log);\nkernel/bpf/syscall.c-3134-\tif (err \u003c 0)\n--\nkernel/bpf/verifier.c=59=static bool bpf_global_percpu_ma_set;\nkernel/bpf/verifier.c-60-\nkernel/bpf/verifier.c:61:/* bpf_check() is a static code analyzer that walks eBPF program\nkernel/bpf/verifier.c-62- * instruction by instruction and updates register/stack state.\n--\nkernel/bpf/verifier.c=20984=static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)\n--\nkernel/bpf/verifier.c-21000-/*\nkernel/bpf/verifier.c:21001: * Verify the PKCS#7 signature of a loaded program. Called from bpf_check()\nkernel/bpf/verifier.c-21002- * once the program's metadata maps have been resolved into used_maps, so\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21132-\nkernel/bpf/verifier.c:21133:int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\nkernel/bpf/verifier.c-21134-\t struct bpf_log_attr *attr_log)\n--\nkernel/bpf/verifier.c-21147-\t/* 'struct bpf_verifier_env' can be global, but since it's not small,\nkernel/bpf/verifier.c:21148:\t * allocate/free it every time bpf_check() is called\nkernel/bpf/verifier.c-21149-\t */\n--\nnet/ipv4/syncookies.c=263=static int cookie_tcp_reqsk_init(struct sock *sk, struct sk_buff *skb,\n--\nnet/ipv4/syncookies.c-301-#if IS_ENABLED(CONFIG_BPF)\nnet/ipv4/syncookies.c:302:struct request_sock *cookie_bpf_check(struct sock *sk, struct sk_buff *skb)\nnet/ipv4/syncookies.c-303-{\n--\nnet/ipv4/syncookies.c=405=struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)\n--\nnet/ipv4/syncookies.c-425-\tif (cookie_bpf_ok(skb)) {\nnet/ipv4/syncookies.c:426:\t\treq = cookie_bpf_check(sk, skb);\nnet/ipv4/syncookies.c-427-\t} else {\n--\nnet/ipv6/syncookies.c=174=struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)\n--\nnet/ipv6/syncookies.c-192-\tif (cookie_bpf_ok(skb)) {\nnet/ipv6/syncookies.c:193:\t\treq = cookie_bpf_check(sk, skb);\nnet/ipv6/syncookies.c-194-\t} else {\n--\ntools/testing/selftests/bpf/prog_tests/signed_loader.c=574=static void signature_failure_logs(void)\n--\ntools/testing/selftests/bpf/prog_tests/signed_loader.c-582-\t\t/*\ntools/testing/selftests/bpf/prog_tests/signed_loader.c:583:\t\t * Signature verification now runs inside bpf_check(), so a\ntools/testing/selftests/bpf/prog_tests/signed_loader.c-584-\t\t * failure is reported through the verifier log. A present-but-\n"
} |
| 29/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 40796 output: 23 thoughts: 249 Thoughts: **My Verification Strategy for BPF Keyring Enforcement** Okay, so I'm digging into this BPF keyring enforcement and need to ensure I've got all the pieces. My initial focus is on the crucial functions and their relationships. `bpf_check` in `kernel/bpf/verifier.c` is the key entry point, specifically calling `bpf_prog_verify_signature()`. It seems like I need to pay attention to that call. Then, `lookup_user_key` in `security/keys/process_keys.c` is going to be important as it handles resolving the keys and keyrings based on user ID. That looks like the function that handles `KEY_SPEC_BPF_KEYRING` too. Next up, `bpf_lookup_keyring` in `kernel/bpf/keys.c` appears to be the function that *actually* does the BPF keyring lookup. Finally, `bpf_keyring_enforced`, also in `kernel/bpf/keys.c`, presumably checks if this whole keyring thing is even enabled or not. Makes sense. Now, let's circle back to the necessary configs. It’s critical that all the necessary components are configured. I need to make sure `CONFIG_KEYS` is enabled. Let's see how that's used. Based on `kernel/bpf/Makefile`, the `keys.o` object is included if `CONFIG_KEYS` is set, and if `CONFIG_BPF_SYSCALL` is also enabled. That's a good first check. Next, I see that `security/keys/process_keys.c` relies on `CONFIG_KEYS`, which means I need to double-check that this feature is enabled. Looking at the `security/Makefile` and `security/keys/Makefile` is the next step to confirm this. I need to trace through the dependency chain there to ensure everything is set up correctly for keyring enforcement. |
| 30/2 | 2026/08/28 18:32 | tool | grepper |
0mArgs:{
"Expression": "keys",
"PathPrefix": "security/"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 1405 lines.\nUse more precise expression if possible.\n\nsecurity/Kconfig=6=menu \"Security options\"\nsecurity/Kconfig-7-\nsecurity/Kconfig:8:source \"security/keys/Kconfig\"\nsecurity/Kconfig-9-\n--\nsecurity/Makefile-5-\nsecurity/Makefile:6:obj-$(CONFIG_KEYS)\t\t\t+= keys/\nsecurity/Makefile-7-\n--\nsecurity/integrity/Kconfig=19=config INTEGRITY_SIGNATURE\n--\nsecurity/integrity/Kconfig-28-\t Different keyrings improves search performance, but also allow\nsecurity/integrity/Kconfig:29:\t to \"lock\" certain keyring to prevent adding new keys.\nsecurity/integrity/Kconfig:30:\t This is useful for evm and module keyrings, when keys are\nsecurity/integrity/Kconfig-31-\t usually only added from initramfs.\n--\nsecurity/integrity/Kconfig=33=config INTEGRITY_ASYMMETRIC_KEYS\nsecurity/integrity/Kconfig:34:\tbool \"Enable asymmetric keys support\"\nsecurity/integrity/Kconfig-35-\tdepends on INTEGRITY_SIGNATURE\n--\nsecurity/integrity/Kconfig-43-\t This option enables digital signature verification using\nsecurity/integrity/Kconfig:44:\t asymmetric keys.\nsecurity/integrity/Kconfig-45-\nsecurity/integrity/Kconfig=46=config INTEGRITY_TRUSTED_KEYRING\nsecurity/integrity/Kconfig:47:\tbool \"Require all keys on the integrity keyrings be signed\"\nsecurity/integrity/Kconfig-48-\tdepends on SYSTEM_TRUSTED_KEYRING\n--\nsecurity/integrity/Kconfig-51-\thelp\nsecurity/integrity/Kconfig:52:\t This option requires that all keys added to the .ima and\nsecurity/integrity/Kconfig-53-\t .evm keyrings be signed by a key on the system trusted\n--\nsecurity/integrity/Kconfig=56=config INTEGRITY_PLATFORM_KEYRING\nsecurity/integrity/Kconfig:57:\tbool \"Provide keyring for platform/firmware trusted keys\"\nsecurity/integrity/Kconfig-58-\tdepends on INTEGRITY_ASYMMETRIC_KEYS\n--\nsecurity/integrity/Kconfig-60-\thelp\nsecurity/integrity/Kconfig:61:\t Provide a separate, distinct keyring for platform trusted keys, which\nsecurity/integrity/Kconfig-62-\t the kernel automatically populates during initialization from values\n--\nsecurity/integrity/Kconfig=66=config INTEGRITY_MACHINE_KEYRING\n--\nsecurity/integrity/Kconfig-73-\t If set, provide a keyring to which Machine Owner Keys (MOK) may\nsecurity/integrity/Kconfig:74:\t be added. This keyring shall contain just MOK keys. Unlike keys\nsecurity/integrity/Kconfig:75:\t in the platform keyring, keys contained in the .machine keyring will\nsecurity/integrity/Kconfig-76-\t be trusted within the kernel.\n--\nsecurity/integrity/Kconfig=78=config INTEGRITY_CA_MACHINE_KEYRING\n--\nsecurity/integrity/Kconfig-85-\t and all Machine Owner Keys (MOK) are added to the machine keyring.\nsecurity/integrity/Kconfig:86:\t If enabled only CA keys are added to the machine keyring, all\nsecurity/integrity/Kconfig:87:\t other MOK keys load into the platform keyring.\nsecurity/integrity/Kconfig-88-\nsecurity/integrity/Kconfig=89=config INTEGRITY_CA_MACHINE_KEYRING_MAX\nsecurity/integrity/Kconfig:90:\tbool \"Only CA keys without DigitialSignature usage set\"\nsecurity/integrity/Kconfig-91-\tdepends on INTEGRITY_CA_MACHINE_KEYRING\n--\nsecurity/integrity/Kconfig-93-\thelp\nsecurity/integrity/Kconfig:94:\t When selected, only load CA keys are loaded into the machine\nsecurity/integrity/Kconfig-95-\t keyring that contain the CA bit set along with the keyCertSign\nsecurity/integrity/Kconfig-96-\t Usage field. Keys containing the digitialSignature Usage field\nsecurity/integrity/Kconfig:97:\t will not be loaded. The remaining MOK keys are loaded into the\nsecurity/integrity/Kconfig-98-\t .platform keyring.\n--\nsecurity/integrity/Kconfig=110=config LOAD_PPC_KEYS\nsecurity/integrity/Kconfig:111:\tbool \"Enable loading of platform and blacklisted keys for POWER\"\nsecurity/integrity/Kconfig-112-\tdepends on INTEGRITY_PLATFORM_KEYRING\n--\nsecurity/integrity/Kconfig-115-\thelp\nsecurity/integrity/Kconfig:116:\t Enable loading of keys to the .platform keyring and blacklisted\nsecurity/integrity/Kconfig-117-\t hashes to the .blacklist keyring for powerpc based platforms.\n--\nsecurity/integrity/digsig.c-17-#include \u003ccrypto/public_key.h\u003e\nsecurity/integrity/digsig.c:18:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/digsig.c-19-\n--\nsecurity/integrity/digsig.c=100=static int __init __integrity_init_keyring(const unsigned int id,\n--\nsecurity/integrity/digsig.c-116-\t\tif (id == INTEGRITY_KEYRING_PLATFORM)\nsecurity/integrity/digsig.c:117:\t\t\tset_platform_trusted_keys(keyring[id]);\nsecurity/integrity/digsig.c-118-\t\tif (id == INTEGRITY_KEYRING_MACHINE \u0026\u0026 imputed_trust_enabled())\nsecurity/integrity/digsig.c:119:\t\t\tset_machine_trusted_keys(keyring[id]);\nsecurity/integrity/digsig.c-120-\t\tif (id == INTEGRITY_KEYRING_IMA)\n--\nsecurity/integrity/digsig.c=127=int __init integrity_init_keyring(const unsigned int id)\n--\nsecurity/integrity/digsig.c-155-\t/*\nsecurity/integrity/digsig.c:156:\t * MOK keys can only be added through a read-only runtime services\nsecurity/integrity/digsig.c:157:\t * UEFI variable during boot. No additional keys shall be allowed to\nsecurity/integrity/digsig.c-158-\t * load into the machine keyring following init from userspace.\n--\nsecurity/integrity/digsig_asymmetric.c-13-#include \u003ccrypto/hash_info.h\u003e\nsecurity/integrity/digsig_asymmetric.c:14:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/digsig_asymmetric.c:15:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/digsig_asymmetric.c-16-\n--\nsecurity/integrity/evm/Kconfig=71=config EVM_X509_PATH\n--\nsecurity/integrity/evm/Kconfig-73-\tdepends on EVM_LOAD_X509\nsecurity/integrity/evm/Kconfig:74:\tdefault \"/etc/keys/x509_evm.der\"\nsecurity/integrity/evm/Kconfig-75-\thelp\n--\nsecurity/integrity/evm/evm_crypto.c-19-#include \u003clinux/evm.h\u003e\nsecurity/integrity/evm/evm_crypto.c:20:#include \u003ckeys/encrypted-type.h\u003e\nsecurity/integrity/evm/evm_crypto.c-21-#include \u003ccrypto/hash.h\u003e\n--\nsecurity/integrity/evm/evm_crypto.c=39=static const char evm_hmac[] = \"hmac(sha1)\";\n--\nsecurity/integrity/evm/evm_crypto.c-46- * This function allows setting the EVM HMAC key from the kernel\nsecurity/integrity/evm/evm_crypto.c:47: * without using the \"encrypted\" key subsystem keys. It can be used\nsecurity/integrity/evm/evm_crypto.c-48- * by the crypto HW kernel module which has its own way of managing\nsecurity/integrity/evm/evm_crypto.c:49: * keys.\nsecurity/integrity/evm/evm_crypto.c-50- *\n--\nsecurity/integrity/iint.c-9- *\t- initialize the integrity directory in securityfs\nsecurity/integrity/iint.c:10: *\t- load IMA and EVM keys\nsecurity/integrity/iint.c-11- */\n--\nsecurity/integrity/iint.c=25=int integrity_kernel_read(struct file *file, loff_t offset,\n--\nsecurity/integrity/iint.c-31-/*\nsecurity/integrity/iint.c:32: * integrity_load_keys - load integrity keys hook\nsecurity/integrity/iint.c-33- *\n--\nsecurity/integrity/iint.c-36- */\nsecurity/integrity/iint.c:37:void __init integrity_load_keys(void)\nsecurity/integrity/iint.c-38-{\n--\nsecurity/integrity/ima/Kconfig=246=config IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY\nsecurity/integrity/ima/Kconfig:247:\tbool \"Permit keys validly signed by a built-in, machine (if configured) or secondary\"\nsecurity/integrity/ima/Kconfig-248-\tdepends on SYSTEM_TRUSTED_KEYRING\n--\nsecurity/integrity/ima/Kconfig-258-\nsecurity/integrity/ima/Kconfig:259:\t Intermediate keys between those the kernel has compiled in and the\nsecurity/integrity/ima/Kconfig:260:\t IMA keys to be added may be added to the system secondary keyring,\nsecurity/integrity/ima/Kconfig-261-\t provided they are validly signed by a key already resident in the\n--\nsecurity/integrity/ima/Kconfig=264=config IMA_BLACKLIST_KEYRING\n--\nsecurity/integrity/ima/Kconfig-270-\t This option creates an IMA blacklist keyring, which contains all\nsecurity/integrity/ima/Kconfig:271:\t revoked IMA keys. It is consulted before any other keyring. If\nsecurity/integrity/ima/Kconfig-272-\t the search is successful the requested operation is rejected and\n--\nsecurity/integrity/ima/Kconfig=275=config IMA_LOAD_X509\n--\nsecurity/integrity/ima/Kconfig-279-\thelp\nsecurity/integrity/ima/Kconfig:280:\t File signature verification is based on the public keys\nsecurity/integrity/ima/Kconfig:281:\t loaded on the .ima trusted keyring. These public keys are\nsecurity/integrity/ima/Kconfig-282-\t X509 certificates signed by a trusted key on the\n--\nsecurity/integrity/ima/Kconfig=286=config IMA_X509_PATH\n--\nsecurity/integrity/ima/Kconfig-288-\tdepends on IMA_LOAD_X509\nsecurity/integrity/ima/Kconfig:289:\tdefault \"/etc/keys/x509_ima.der\"\nsecurity/integrity/ima/Kconfig-290-\thelp\n--\nsecurity/integrity/ima/Makefile=14=ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o\nsecurity/integrity/ima/Makefile:15:ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o\nsecurity/integrity/ima/Makefile:16:ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o\nsecurity/integrity/ima/Makefile-17-\n--\nsecurity/integrity/ima/ima.h=399=struct modsig;\n--\nsecurity/integrity/ima/ima.h-402-/*\nsecurity/integrity/ima/ima.h:403: * To track keys that need to be measured.\nsecurity/integrity/ima/ima.h-404- */\n--\nsecurity/integrity/ima/ima.h=413=bool ima_queue_key(struct key *keyring, const void *payload,\nsecurity/integrity/ima/ima.h-414-\t\t size_t payload_len);\nsecurity/integrity/ima/ima.h:415:void ima_process_queued_keys(void);\nsecurity/integrity/ima/ima.h-416-#else\n--\nsecurity/integrity/ima/ima.h=419=static inline bool ima_queue_key(struct key *keyring,\n--\nsecurity/integrity/ima/ima.h-421-\t\t\t\t size_t payload_len) { return false; }\nsecurity/integrity/ima/ima.h:422:static inline void ima_process_queued_keys(void) {}\nsecurity/integrity/ima/ima.h-423-#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */\n--\nsecurity/integrity/ima/ima_appraise.c-17-#include \u003clinux/fsverity.h\u003e\nsecurity/integrity/ima/ima_appraise.c:18:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/ima/ima_appraise.c-19-#include \u003cuapi/linux/fsverity.h\u003e\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c-6- *\nsecurity/integrity/ima/ima_asymmetric_keys.c:7: * File: ima_asymmetric_keys.c\nsecurity/integrity/ima/ima_asymmetric_keys.c:8: * Defines an IMA hook to measure asymmetric keys on key\nsecurity/integrity/ima/ima_asymmetric_keys.c-9- * create or update.\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c-11-\nsecurity/integrity/ima/ima_asymmetric_keys.c:12:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/ima/ima_asymmetric_keys.c-13-#include \u003clinux/user_namespace.h\u003e\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c-17-/**\nsecurity/integrity/ima/ima_asymmetric_keys.c:18: * ima_post_key_create_or_update - measure asymmetric keys\nsecurity/integrity/ima/ima_asymmetric_keys.c-19- * @keyring: keyring to which the key is linked to\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c=29=void ima_post_key_create_or_update(struct key *keyring, struct key *key,\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c-34-\nsecurity/integrity/ima/ima_asymmetric_keys.c:35:\t/* Only asymmetric keys are handled by this hook. */\nsecurity/integrity/ima/ima_asymmetric_keys.c-36-\tif (key-\u003etype != \u0026key_type_asymmetric)\n--\nsecurity/integrity/ima/ima_asymmetric_keys.c-49-\t * keyring-\u003edescription points to the name of the keyring\nsecurity/integrity/ima/ima_asymmetric_keys.c:50:\t * (such as \".builtin_trusted_keys\", \".ima\", etc.) to\nsecurity/integrity/ima/ima_asymmetric_keys.c-51-\t * which the given key is linked to.\n--\nsecurity/integrity/ima/ima_modsig.c-12-#include \u003clinux/module_signature.h\u003e\nsecurity/integrity/ima/ima_modsig.c:13:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/ima/ima_modsig.c-14-#include \u003ccrypto/pkcs7.h\u003e\n--\nsecurity/integrity/ima/ima_mok.c-15-#include \u003clinux/slab.h\u003e\nsecurity/integrity/ima/ima_mok.c:16:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/ima/ima_mok.c-17-\n--\nsecurity/integrity/ima/ima_policy.c=102=struct ima_rule_entry {\n--\nsecurity/integrity/ima/ima_policy.c-126-\tchar *fs_subtype;\nsecurity/integrity/ima/ima_policy.c:127:\tstruct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */\nsecurity/integrity/ima/ima_policy.c-128-\tstruct ima_rule_opt_list *label; /* Measure data grouped under this label */\n--\nsecurity/integrity/ima/ima_policy.c=1059=void ima_update_policy(void)\n--\nsecurity/integrity/ima/ima_policy.c-1079-\t/* Custom IMA policy has been loaded */\nsecurity/integrity/ima/ima_policy.c:1080:\tima_process_queued_keys();\nsecurity/integrity/ima/ima_policy.c-1081-}\n--\nsecurity/integrity/ima/ima_policy.c=2123=int ima_policy_show(struct seq_file *m, void *v)\n--\nsecurity/integrity/ima/ima_policy.c-2342- * an IMA digital signature. This is restricted to cases where the kernel\nsecurity/integrity/ima/ima_policy.c:2343: * has a set of built-in trusted keys in order to avoid an attacker simply\nsecurity/integrity/ima/ima_policy.c:2344: * loading additional keys.\nsecurity/integrity/ima/ima_policy.c-2345- */\n--\nsecurity/integrity/ima/ima_queue_keys.c-6- *\nsecurity/integrity/ima/ima_queue_keys.c:7: * File: ima_queue_keys.c\nsecurity/integrity/ima/ima_queue_keys.c:8: * Enables deferred processing of keys\nsecurity/integrity/ima/ima_queue_keys.c-9- */\n--\nsecurity/integrity/ima/ima_queue_keys.c-12-#include \u003clinux/workqueue.h\u003e\nsecurity/integrity/ima/ima_queue_keys.c:13:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/ima/ima_queue_keys.c-14-#include \"ima.h\"\n--\nsecurity/integrity/ima/ima_queue_keys.c-19- */\nsecurity/integrity/ima/ima_queue_keys.c:20:static bool ima_process_keys;\nsecurity/integrity/ima/ima_queue_keys.c-21-\nsecurity/integrity/ima/ima_queue_keys.c-22-/*\nsecurity/integrity/ima/ima_queue_keys.c:23: * To synchronize access to the list of keys that need to be measured\nsecurity/integrity/ima/ima_queue_keys.c-24- */\nsecurity/integrity/ima/ima_queue_keys.c:25:static DEFINE_MUTEX(ima_keys_lock);\nsecurity/integrity/ima/ima_queue_keys.c:26:static LIST_HEAD(ima_keys);\nsecurity/integrity/ima/ima_queue_keys.c-27-\nsecurity/integrity/ima/ima_queue_keys.c-28-/*\nsecurity/integrity/ima/ima_queue_keys.c:29: * If custom IMA policy is not loaded then keys queued up\nsecurity/integrity/ima/ima_queue_keys.c-30- * for measurement should be freed. This worker is used\n--\nsecurity/integrity/ima/ima_queue_keys.c=33=static long ima_key_queue_timeout = 300000; /* 5 Minutes */\nsecurity/integrity/ima/ima_queue_keys.c:34:static void ima_keys_handler(struct work_struct *work);\nsecurity/integrity/ima/ima_queue_keys.c:35:static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);\nsecurity/integrity/ima/ima_queue_keys.c-36-static bool timer_expired;\n--\nsecurity/integrity/ima/ima_queue_keys.c-38-/*\nsecurity/integrity/ima/ima_queue_keys.c:39: * This worker function frees keys that may still be\nsecurity/integrity/ima/ima_queue_keys.c-40- * queued up in case custom IMA policy was not loaded.\nsecurity/integrity/ima/ima_queue_keys.c-41- */\nsecurity/integrity/ima/ima_queue_keys.c:42:static void ima_keys_handler(struct work_struct *work)\nsecurity/integrity/ima/ima_queue_keys.c-43-{\nsecurity/integrity/ima/ima_queue_keys.c-44-\ttimer_expired = true;\nsecurity/integrity/ima/ima_queue_keys.c:45:\tima_process_queued_keys();\nsecurity/integrity/ima/ima_queue_keys.c-46-}\n--\nsecurity/integrity/ima/ima_queue_keys.c-48-/*\nsecurity/integrity/ima/ima_queue_keys.c:49: * This function sets up a worker to free queued keys in case\nsecurity/integrity/ima/ima_queue_keys.c-50- * custom IMA policy was never loaded.\n--\nsecurity/integrity/ima/ima_queue_keys.c=52=void ima_init_key_queue(void)\nsecurity/integrity/ima/ima_queue_keys.c-53-{\nsecurity/integrity/ima/ima_queue_keys.c:54:\tschedule_delayed_work(\u0026ima_keys_delayed_work,\nsecurity/integrity/ima/ima_queue_keys.c-55-\t\t\t msecs_to_jiffies(ima_key_queue_timeout));\n--\nsecurity/integrity/ima/ima_queue_keys.c=104=bool ima_queue_key(struct key *keyring, const void *payload,\n--\nsecurity/integrity/ima/ima_queue_keys.c-113-\nsecurity/integrity/ima/ima_queue_keys.c:114:\tmutex_lock(\u0026ima_keys_lock);\nsecurity/integrity/ima/ima_queue_keys.c:115:\tif (!ima_process_keys) {\nsecurity/integrity/ima/ima_queue_keys.c:116:\t\tlist_add_tail(\u0026entry-\u003elist, \u0026ima_keys);\nsecurity/integrity/ima/ima_queue_keys.c-117-\t\tqueued = true;\nsecurity/integrity/ima/ima_queue_keys.c-118-\t}\nsecurity/integrity/ima/ima_queue_keys.c:119:\tmutex_unlock(\u0026ima_keys_lock);\nsecurity/integrity/ima/ima_queue_keys.c-120-\n--\nsecurity/integrity/ima/ima_queue_keys.c-127-/*\nsecurity/integrity/ima/ima_queue_keys.c:128: * ima_process_queued_keys() - process keys queued for measurement\nsecurity/integrity/ima/ima_queue_keys.c-129- *\nsecurity/integrity/ima/ima_queue_keys.c:130: * This function sets ima_process_keys to true and processes queued keys.\nsecurity/integrity/ima/ima_queue_keys.c:131: * From here on keys will be processed right away (not queued).\nsecurity/integrity/ima/ima_queue_keys.c-132- */\nsecurity/integrity/ima/ima_queue_keys.c:133:void ima_process_queued_keys(void)\nsecurity/integrity/ima/ima_queue_keys.c-134-{\n--\nsecurity/integrity/ima/ima_queue_keys.c-137-\nsecurity/integrity/ima/ima_queue_keys.c:138:\tif (ima_process_keys)\nsecurity/integrity/ima/ima_queue_keys.c-139-\t\treturn;\n--\nsecurity/integrity/ima/ima_queue_keys.c-141-\t/*\nsecurity/integrity/ima/ima_queue_keys.c:142:\t * Since ima_process_keys is set to true, any new key will be\nsecurity/integrity/ima/ima_queue_keys.c:143:\t * processed immediately and not be queued to ima_keys list.\nsecurity/integrity/ima/ima_queue_keys.c:144:\t * First one setting the ima_process_keys flag to true will\nsecurity/integrity/ima/ima_queue_keys.c:145:\t * process the queued keys.\nsecurity/integrity/ima/ima_queue_keys.c-146-\t */\nsecurity/integrity/ima/ima_queue_keys.c:147:\tmutex_lock(\u0026ima_keys_lock);\nsecurity/integrity/ima/ima_queue_keys.c:148:\tif (!ima_process_keys) {\nsecurity/integrity/ima/ima_queue_keys.c:149:\t\tima_process_keys = true;\nsecurity/integrity/ima/ima_queue_keys.c-150-\t\tprocess = true;\nsecurity/integrity/ima/ima_queue_keys.c-151-\t}\nsecurity/integrity/ima/ima_queue_keys.c:152:\tmutex_unlock(\u0026ima_keys_lock);\nsecurity/integrity/ima/ima_queue_keys.c-153-\n--\nsecurity/integrity/ima/ima_queue_keys.c-157-\tif (!timer_expired)\nsecurity/integrity/ima/ima_queue_keys.c:158:\t\tcancel_delayed_work_sync(\u0026ima_keys_delayed_work);\nsecurity/integrity/ima/ima_queue_keys.c-159-\nsecurity/integrity/ima/ima_queue_keys.c:160:\tlist_for_each_entry_safe(entry, tmp, \u0026ima_keys, list) {\nsecurity/integrity/ima/ima_queue_keys.c-161-\t\tif (!timer_expired)\n--\nsecurity/integrity/ima/ima_queue_keys.c=174=inline bool ima_should_queue_key(void)\nsecurity/integrity/ima/ima_queue_keys.c-175-{\nsecurity/integrity/ima/ima_queue_keys.c:176:\treturn !ima_process_keys;\nsecurity/integrity/ima/ima_queue_keys.c-177-}\n--\nsecurity/integrity/integrity.h=78=struct ima_max_digest_data {\n--\nsecurity/integrity/integrity.h-83-/*\nsecurity/integrity/integrity.h:84: * signature header format v2 - for using with asymmetric keys\nsecurity/integrity/integrity.h-85- *\n--\nsecurity/integrity/platform_certs/keyring_handler.c-8-#include \u003clinux/slab.h\u003e\nsecurity/integrity/platform_certs/keyring_handler.c:9:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/platform_certs/keyring_handler.c:10:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/platform_certs/keyring_handler.c-11-#include \"../integrity.h\"\n--\nsecurity/integrity/platform_certs/keyring_handler.c=61=__init efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type)\n--\nsecurity/integrity/platform_certs/keyring_handler.c-72-\nsecurity/integrity/platform_certs/keyring_handler.c:73:__init efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type)\nsecurity/integrity/platform_certs/keyring_handler.c-74-{\n--\nsecurity/integrity/platform_certs/keyring_handler.c-80-\nsecurity/integrity/platform_certs/keyring_handler.c:81:__init efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type)\nsecurity/integrity/platform_certs/keyring_handler.c-82-{\n--\nsecurity/integrity/platform_certs/keyring_handler.h=30=efi_element_handler_t get_handler_for_mok(const efi_guid_t *sig_type);\n--\nsecurity/integrity/platform_certs/keyring_handler.h-32-/*\nsecurity/integrity/platform_certs/keyring_handler.h:33: * Return the handler for particular signature list types for CA keys.\nsecurity/integrity/platform_certs/keyring_handler.h-34- */\nsecurity/integrity/platform_certs/keyring_handler.h:35:efi_element_handler_t get_handler_for_ca_keys(const efi_guid_t *sig_type);\nsecurity/integrity/platform_certs/keyring_handler.h-36-\nsecurity/integrity/platform_certs/keyring_handler.h-37-/*\nsecurity/integrity/platform_certs/keyring_handler.h:38: * Return the handler for particular signature list types for code signing keys.\nsecurity/integrity/platform_certs/keyring_handler.h-39- */\nsecurity/integrity/platform_certs/keyring_handler.h:40:efi_element_handler_t get_handler_for_code_signing_keys(const efi_guid_t *sig_type);\nsecurity/integrity/platform_certs/keyring_handler.h-41-\n--\nsecurity/integrity/platform_certs/load_ipl_s390.c-8-#include \u003clinux/slab.h\u003e\nsecurity/integrity/platform_certs/load_ipl_s390.c:9:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/platform_certs/load_ipl_s390.c:10:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/platform_certs/load_ipl_s390.c-11-#include \u003casm/boot_data.h\u003e\n--\nsecurity/integrity/platform_certs/load_powerpc.c-5- *\nsecurity/integrity/platform_certs/load_powerpc.c:6: * - loads keys and hashes stored and controlled by the firmware.\nsecurity/integrity/platform_certs/load_powerpc.c-7- */\n--\nsecurity/integrity/platform_certs/load_powerpc.c=29=static __init void *get_cert_list(u8 *key, unsigned long keylen, u64 *size)\n--\nsecurity/integrity/platform_certs/load_powerpc.c-54-/*\nsecurity/integrity/platform_certs/load_powerpc.c:55: * Load the certs contained in the keys databases into the platform trusted\nsecurity/integrity/platform_certs/load_powerpc.c-56- * keyring and the blacklisted X.509 cert SHA256 hashes into the blacklist\n--\nsecurity/integrity/platform_certs/load_powerpc.c=59=static int __init load_powerpc_certs(void)\n--\nsecurity/integrity/platform_certs/load_powerpc.c-135-\t\trc = parse_efi_signature_list(\"powerpc:trustedca\", trustedca, dsize,\nsecurity/integrity/platform_certs/load_powerpc.c:136:\t\t\t\t\t get_handler_for_ca_keys);\nsecurity/integrity/platform_certs/load_powerpc.c-137-\t\tif (rc)\n--\nsecurity/integrity/platform_certs/load_powerpc.c-151-\t\trc = parse_efi_signature_list(\"powerpc:moduledb\", moduledb, dsize,\nsecurity/integrity/platform_certs/load_powerpc.c:152:\t\t\t\t\t get_handler_for_code_signing_keys);\nsecurity/integrity/platform_certs/load_powerpc.c-153-\t\tif (rc)\n--\nsecurity/integrity/platform_certs/load_uefi.c-10-#include \u003clinux/ima.h\u003e\nsecurity/integrity/platform_certs/load_uefi.c:11:#include \u003ckeys/asymmetric-type.h\u003e\nsecurity/integrity/platform_certs/load_uefi.c:12:#include \u003ckeys/system_keyring.h\u003e\nsecurity/integrity/platform_certs/load_uefi.c-13-#include \"../integrity.h\"\n--\nsecurity/integrity/platform_certs/machine_keyring.c=24=void __init add_to_machine_keyring(const char *source, const void *data, size_t len)\n--\nsecurity/integrity/platform_certs/machine_keyring.c-32-\t/*\nsecurity/integrity/platform_certs/machine_keyring.c:33:\t * Some MOKList keys may not pass the machine keyring restrictions.\nsecurity/integrity/platform_certs/machine_keyring.c-34-\t * If the restriction check does not pass and the platform keyring\n--\nsecurity/integrity/platform_certs/machine_keyring.c-42-\tif (rc)\nsecurity/integrity/platform_certs/machine_keyring.c:43:\t\tpr_info(\"Error adding keys to machine keyring %s\\n\", source);\nsecurity/integrity/platform_certs/machine_keyring.c-44-}\n--\nsecurity/integrity/platform_certs/machine_keyring.c-47- * Try to load the MokListTrustedRT MOK variable to see if we should trust\nsecurity/integrity/platform_certs/machine_keyring.c:48: * the MOK keys within the kernel. It is not an error if this variable\nsecurity/integrity/platform_certs/machine_keyring.c:49: * does not exist. If it does not exist, MOK keys should not be trusted\nsecurity/integrity/platform_certs/machine_keyring.c-50- * within the machine keyring.\nsecurity/integrity/platform_certs/machine_keyring.c-51- */\nsecurity/integrity/platform_certs/machine_keyring.c:52:static __init bool uefi_check_trust_mok_keys(void)\nsecurity/integrity/platform_certs/machine_keyring.c-53-{\n--\nsecurity/integrity/platform_certs/machine_keyring.c=64=static bool __init trust_moklist(void)\n--\nsecurity/integrity/platform_certs/machine_keyring.c-72-\nsecurity/integrity/platform_certs/machine_keyring.c:73:\t\tif (uefi_check_trust_mok_keys())\nsecurity/integrity/platform_certs/machine_keyring.c-74-\t\t\ttrust_mok = true;\n--\nsecurity/integrity/platform_certs/machine_keyring.c-80-/*\nsecurity/integrity/platform_certs/machine_keyring.c:81: * Provides platform specific check for trusting imputed keys before loading\nsecurity/integrity/platform_certs/machine_keyring.c-82- * on .machine keyring. UEFI systems enable this trust based on a variable,\n--\nsecurity/integrity/platform_certs/platform_keyring.c-2-/*\nsecurity/integrity/platform_certs/platform_keyring.c:3: * Platform keyring for firmware/platform keys\nsecurity/integrity/platform_certs/platform_keyring.c-4- *\n--\nsecurity/integrity/platform_certs/platform_keyring.c=26=void __init add_to_platform_keyring(const char *source, const void *data,\n--\nsecurity/integrity/platform_certs/platform_keyring.c-36-\tif (rc)\nsecurity/integrity/platform_certs/platform_keyring.c:37:\t\tpr_info(\"Error adding keys to platform keyring %s\\n\", source);\nsecurity/integrity/platform_certs/platform_keyring.c-38-}\n--\nsecurity/integrity/platform_certs/platform_keyring.c=43=static __init int platform_keyring_init(void)\n--\nsecurity/integrity/platform_certs/platform_keyring.c-55-/*\nsecurity/integrity/platform_certs/platform_keyring.c:56: * Must be initialised before we try and load the keys into the keyring.\nsecurity/integrity/platform_certs/platform_keyring.c-57- */\n--\nsecurity/keys/Kconfig=6=menuconfig KEYS\n--\nsecurity/keys/Kconfig-10-\t This option provides support for retaining authentication tokens and\nsecurity/keys/Kconfig:11:\t access keys in the kernel.\nsecurity/keys/Kconfig-12-\nsecurity/keys/Kconfig:13:\t It also includes provision of methods by which such keys might be\nsecurity/keys/Kconfig-14-\t associated with a process so that network filesystems, encryption\n--\nsecurity/keys/Kconfig-17-\t Furthermore, a special type of key is available that acts as keyring:\nsecurity/keys/Kconfig:18:\t a searchable sequence of keys. Each process is equipped with access\nsecurity/keys/Kconfig-19-\t to five standard keyrings: UID-specific, GID-specific, session,\n--\nsecurity/keys/Kconfig=59=config BIG_KEYS\nsecurity/keys/Kconfig:60:\tbool \"Large payload keys\"\nsecurity/keys/Kconfig-61-\tdepends on TMPFS\n--\nsecurity/keys/Kconfig-63-\thelp\nsecurity/keys/Kconfig:64:\t This option provides support for holding large keys within the kernel\nsecurity/keys/Kconfig-65-\t (for example Kerberos ticket caches). The data may be stored out to\n--\nsecurity/keys/Kconfig=70=config TRUSTED_KEYS\n--\nsecurity/keys/Kconfig-73-\t This option provides support for creating, sealing, and unsealing\nsecurity/keys/Kconfig:74:\t keys in the kernel. Trusted keys are random number symmetric keys,\nsecurity/keys/Kconfig-75-\t generated and sealed by a trust source selected at kernel boot-time.\n--\nsecurity/keys/Kconfig=80=if TRUSTED_KEYS\nsecurity/keys/Kconfig:81:source \"security/keys/trusted-keys/Kconfig\"\nsecurity/keys/Kconfig-82-endif\n--\nsecurity/keys/Kconfig=84=config ENCRYPTED_KEYS\n--\nsecurity/keys/Kconfig-90-\thelp\nsecurity/keys/Kconfig:91:\t This option provides support for create/encrypting/decrypting keys\nsecurity/keys/Kconfig:92:\t in the kernel. Encrypted keys are instantiated using kernel\nsecurity/keys/Kconfig-93-\t generated random numbers or provided decrypted data, and are\n--\nsecurity/keys/Kconfig=100=config USER_DECRYPTED_DATA\nsecurity/keys/Kconfig:101:\tbool \"Allow encrypted keys with user decrypted data\"\nsecurity/keys/Kconfig-102-\tdepends on ENCRYPTED_KEYS\nsecurity/keys/Kconfig-103-\thelp\nsecurity/keys/Kconfig:104:\t This option provides support for instantiating encrypted keys using\nsecurity/keys/Kconfig-105-\t user-provided decrypted data. The decrypted data must be hex-ascii\n--\nsecurity/keys/Kconfig=110=config KEY_DH_OPERATIONS\nsecurity/keys/Kconfig:111: bool \"Diffie-Hellman operations on retained keys\"\nsecurity/keys/Kconfig-112- select CRYPTO\n--\nsecurity/keys/Kconfig-116-\t This option provides support for calculating Diffie-Hellman\nsecurity/keys/Kconfig:117:\t public keys and shared secrets using values stored as keys\nsecurity/keys/Kconfig-118-\t in the kernel.\n--\nsecurity/keys/Kconfig=122=config KEY_NOTIFICATIONS\n--\nsecurity/keys/Kconfig-126-\t This option provides support for getting change notifications\nsecurity/keys/Kconfig:127:\t on keys and keyrings on which the caller has View permission.\nsecurity/keys/Kconfig-128-\t This makes use of pipes to handle the notification buffer and\n--\nsecurity/keys/Makefile=9=obj-y := \\\n--\nsecurity/keys/Makefile-14-\tpermission.o \\\nsecurity/keys/Makefile:15:\tprocess_keys.o \\\nsecurity/keys/Makefile-16-\trequest_key.o \\\n--\nsecurity/keys/Makefile=30=obj-$(CONFIG_BIG_KEYS) += big_key.o\nsecurity/keys/Makefile:31:obj-$(CONFIG_TRUSTED_KEYS) += trusted-keys/\n\n"
} |
| 31/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 51383 output: 24 thoughts: 199 Thoughts: **BPF Signature Verification: Configuration Deep Dive** Okay, let's circle back to that `kernel/bpf/Makefile`. I remember we were digging into the signature verification stuff. Let's make sure we have everything lined up correctly. Looking at the `Makefile` snippet, it seems like `CONFIG_BPF_SYSCALL` depends on `CONFIG_KEYS`. So, to get this working, I'll need both `BPF_SYSCALL` and `KEYS` enabled in the kernel configuration. That seems straightforward enough, but I want to make sure I haven't missed anything crucial. Now, let's broaden the search a bit. I need to investigate any related configurations for signature verification or the handling of asymmetric keys within the BPF framework. I should check how `bpf_verify_pkcs7_signature` or even the more generic `verify_pkcs7_signature` are used. I should also investigate other related identifiers like `SYSTEM_DATA_VERIFICATION`, `ASYMMETRIC_KEY_TYPE`, and also ensure I check for cryptographic primitives like `CRYPTO_RSA`, `CRYPTO_MLDSA`, `CRYPTO_SHA256`, and `CRYPTO_SHA512` as their presence might be crucial to the overall functionality. First, let's start with a focused search, specifically within `kernel/bpf/`, looking for any use of `bpf_verify_pkcs7_signature`. This is where I'll probably find the key to understanding the deeper dependencies. |
| 32/2 | 2026/08/28 18:32 | tool | grepper |
0mArgs:{
"Expression": "bpf_verify_pkcs7_signature"
}Results: {
"Output": "include/linux/bpf.h=3825=void bpf_key_put(struct bpf_key *bkey);\ninclude/linux/bpf.h:3826:int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\ninclude/linux/bpf.h-3827-\t\t\t const struct bpf_dynptr *sig_p,\n--\ninclude/linux/bpf.h=3855=static inline void bpf_key_put(struct bpf_key *bkey)\n--\ninclude/linux/bpf.h-3858-\ninclude/linux/bpf.h:3859:static inline int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\ninclude/linux/bpf.h-3860-\t\t\t\t\t const struct bpf_dynptr *sig_p,\n--\nkernel/bpf/helpers.c=4299=__bpf_kfunc void bpf_key_put(struct bpf_key *bkey)\n--\nkernel/bpf/helpers.c-4307-/**\nkernel/bpf/helpers.c:4308: * bpf_verify_pkcs7_signature - verify a PKCS#7 signature\nkernel/bpf/helpers.c-4309- * @data_p: data to verify\n--\nkernel/bpf/helpers.c-4317- */\nkernel/bpf/helpers.c:4318:__bpf_kfunc int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\nkernel/bpf/helpers.c-4319-\t\t\t const struct bpf_dynptr *sig_p,\n--\nkernel/bpf/helpers.c=4861=BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)\nkernel/bpf/helpers.c-4862-#ifdef CONFIG_SYSTEM_DATA_VERIFICATION\nkernel/bpf/helpers.c:4863:BTF_ID_FLAGS(func, bpf_verify_pkcs7_signature, KF_SLEEPABLE)\nkernel/bpf/helpers.c-4864-#endif\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21115-\nkernel/bpf/verifier.c:21116:\terr = bpf_verify_pkcs7_signature((struct bpf_dynptr *)\u0026data_ptr,\nkernel/bpf/verifier.c-21117-\t\t\t\t\t (struct bpf_dynptr *)\u0026sig_ptr, key);\n--\ntools/testing/selftests/bpf/bpf_kfuncs.h=77=extern void bpf_key_put(struct bpf_key *key) __ksym;\ntools/testing/selftests/bpf/bpf_kfuncs.h:78:extern int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_ptr,\ntools/testing/selftests/bpf/bpf_kfuncs.h-79-\t\t\t\t const struct bpf_dynptr *sig_ptr,\n--\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c=22=static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt,\n--\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c-27-\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c:28:\tif (strcmp(va_arg(args, char *), \"bpf_verify_pkcs7_signature\"))\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c-29-\t\treturn 0;\n--\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c=35=static bool has_pkcs7_kfunc_support(void)\n--\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c-52-\t\tfprintf(stderr,\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c:53:\t\t \"%s:SKIP:bpf_verify_pkcs7_signature() kfunc not supported\\n\",\ntools/testing/selftests/bpf/prog_tests/kfunc_dynptr_param.c-54-\t\t __func__);\n--\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c=46=static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt,\n--\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c-54-\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c:55:\tif (strcmp(va_arg(args, char *), \"bpf_verify_pkcs7_signature\"))\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c-56-\t\treturn 0;\n--\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c=242=static void test_verify_pkcs7_sig_from_map(void)\n--\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c-273-\t\tprintf(\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c:274:\t\t \"%s:SKIP:bpf_verify_pkcs7_signature() kfunc not supported\\n\",\ntools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c-275-\t\t __func__);\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c=34=int BPF_PROG(not_valid_dynptr, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-37-\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c:38:\treturn bpf_verify_pkcs7_signature((struct bpf_dynptr *)\u0026val,\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-39-\t\t\t\t\t (struct bpf_dynptr *)\u0026val, NULL);\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c=44=int BPF_PROG(not_ptr_to_stack, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-47-\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c:48:\treturn bpf_verify_pkcs7_signature(\u0026val, \u0026val, NULL);\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-49-}\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c=52=int BPF_PROG(dynptr_data_null, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-74-\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c:75:\terr = bpf_verify_pkcs7_signature(\u0026ptr, \u0026ptr, trusted_keyring);\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-76-\n--\ntools/testing/selftests/bpf/progs/test_sig_in_xattr.c=46=int BPF_PROG(test_file_open, struct file *f)\n--\ntools/testing/selftests/bpf/progs/test_sig_in_xattr.c-79-\t/* Verify signature */\ntools/testing/selftests/bpf/progs/test_sig_in_xattr.c:80:\tret = bpf_verify_pkcs7_signature(\u0026digest_ptr, \u0026sig_ptr, trusted_keyring);\ntools/testing/selftests/bpf/progs/test_sig_in_xattr.c-81-\n--\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c=40=int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c-83-\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c:84:\tret = bpf_verify_pkcs7_signature(\u0026data_ptr, \u0026sig_ptr, trusted_keyring);\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c-85-\n"
} |
| 33/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 54352 output: 37 thoughts: 30 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating `bpf_verify_pkcs7_signature`** Alright, time to get my hands dirty. I need to understand how `bpf_verify_pkcs7_signature` is implemented. I should start by diving into the source code, specifically `kernel/bpf/helpers.c`. That's the obvious place to find it, given it's a BPF helper function. I need to see the nitty-gritty details of this function. What cryptographic algorithms are supported, the memory allocation requirements, the error handling, everything. I want to see if there are any obvious performance bottlenecks or potential security vulnerabilities I should keep in mind as I use this. |
| 34/2 | 2026/08/28 18:32 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/helpers.c",
"FirstLine": 4300,
"LineCount": 40
}Results: {
"Contents": "4300:\t{\n4301:\t\tif (bkey-\u003ehas_ref)\n4302:\t\t\tkey_put(bkey-\u003ekey);\n4303:\t\n4304:\t\tkfree(bkey);\n4305:\t}\n4306:\t\n4307:\t/**\n4308:\t * bpf_verify_pkcs7_signature - verify a PKCS#7 signature\n4309:\t * @data_p: data to verify\n4310:\t * @sig_p: signature of the data\n4311:\t * @trusted_keyring: keyring with keys trusted for signature verification\n4312:\t *\n4313:\t * Verify the PKCS#7 signature *sig_ptr* against the supplied *data_ptr*\n4314:\t * with keys in a keyring referenced by *trusted_keyring*.\n4315:\t *\n4316:\t * Return: 0 on success, a negative value on error.\n4317:\t */\n4318:\t__bpf_kfunc int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\n4319:\t\t\t\t const struct bpf_dynptr *sig_p,\n4320:\t\t\t\t struct bpf_key *trusted_keyring)\n4321:\t{\n4322:\t#ifdef CONFIG_SYSTEM_DATA_VERIFICATION\n4323:\t\tconst struct bpf_dynptr_kern *data_ptr = (struct bpf_dynptr_kern *)data_p;\n4324:\t\tconst struct bpf_dynptr_kern *sig_ptr = (struct bpf_dynptr_kern *)sig_p;\n4325:\t\tconst void *data, *sig;\n4326:\t\tu32 data_len, sig_len;\n4327:\t\tint ret;\n4328:\t\n4329:\t\tif (trusted_keyring-\u003ehas_ref) {\n4330:\t\t\t/*\n4331:\t\t\t * Do the permission check deferred in bpf_lookup_user_key().\n4332:\t\t\t * See bpf_lookup_user_key() for more details.\n4333:\t\t\t *\n4334:\t\t\t * A call to key_task_permission() here would be redundant, as\n4335:\t\t\t * it is already done by keyring_search() called by\n4336:\t\t\t * find_asymmetric_key().\n4337:\t\t\t */\n4338:\t\t\tret = key_validate(trusted_keyring-\u003ekey);\n4339:\t\t\tif (ret \u003c 0)\n"
} |
| 35/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 55255 output: 37 thoughts: 48 Thoughts: Okay, here's my interpretation of that code snippet, delivered as if it's my own thought process: **Diving into `bpf_verify_pkcs7_signature`** Alright, my expert eye caught that `#ifdef CONFIG_SYSTEM_DATA_VERIFICATION` immediately within the `bpf_verify_pkcs7_signature` function. Hmm, always interesting to see conditional compilation like this in low-level code, especially in the BPF world. It's screaming a potential dependency or feature flag to me. Now, the important question is: what's the consequence if `CONFIG_SYSTEM_DATA_VERIFICATION` *isn't* defined? Is this code path going to be skipped entirely? Will there be a fallback mechanism? Perhaps some default behavior is used instead, or maybe it simply fails silently? I need to understand what this verification step actually *does* in relation to the larger picture of BPF code verification and system integrity. Is it core to security, or more of a hardening feature? Depending on the answer, the implications of disabling it could be minor or critical. Let me dig a little deeper... I need to see exactly where this verification is used and the implications of its presence or absence. |
| 36/2 | 2026/08/28 18:32 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/helpers.c",
"FirstLine": 4340,
"LineCount": 25
}Results: {
"Contents": "4340:\t\t\t\treturn ret;\n4341:\t\t}\n4342:\t\n4343:\t\tdata_len = __bpf_dynptr_size(data_ptr);\n4344:\t\tdata = __bpf_dynptr_data(data_ptr, data_len);\n4345:\t\tif (!data)\n4346:\t\t\treturn -EINVAL;\n4347:\t\n4348:\t\tsig_len = __bpf_dynptr_size(sig_ptr);\n4349:\t\tsig = __bpf_dynptr_data(sig_ptr, sig_len);\n4350:\t\tif (!sig)\n4351:\t\t\treturn -EINVAL;\n4352:\t\n4353:\t\treturn verify_pkcs7_signature(data, data_len, sig, sig_len,\n4354:\t\t\t\t\t trusted_keyring-\u003ekey,\n4355:\t\t\t\t\t VERIFYING_BPF_SIGNATURE, NULL,\n4356:\t\t\t\t\t NULL);\n4357:\t#else\n4358:\t\treturn -EOPNOTSUPP;\n4359:\t#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */\n4360:\t}\n4361:\t#endif /* CONFIG_KEYS */\n4362:\t\n4363:\ttypedef int (*bpf_task_work_callback_t)(struct bpf_map *map, void *key, void *value);\n4364:\t\n"
} |
| 37/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 55986 output: 33 thoughts: 37 Thoughts: Here's my thinking process: **Diving into the Kernel Configuration Landscape** Okay, so I need to figure out what `SYSTEM_DATA_VERIFICATION` actually *is* within the kernel's configuration system, specifically in Kconfig. My first step is to locate it in the source. Given my knowledge of the kernel's layout, and based on past experience with Kconfig files, my best bet is the `init/Kconfig` file. I'll zero in around line 2187, as that's often a good starting point for a system-level option like this. I'm expecting to find a definition, maybe some dependencies, and hopefully enough information to understand its purpose and implications within the kernel build. Then I can make my own assumptions and add my experience to the understanding. |
| 38/2 | 2026/08/28 18:32 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 2185,
"LineCount": 25
}Results: {
"Contents": "2185:\tendmenu\n2186:\t\n2187:\tconfig SYSTEM_DATA_VERIFICATION\n2188:\t\tdef_bool n\n2189:\t\tselect SYSTEM_TRUSTED_KEYRING\n2190:\t\tselect KEYS\n2191:\t\tselect CRYPTO\n2192:\t\tselect CRYPTO_RSA\n2193:\t\tselect ASYMMETRIC_KEY_TYPE\n2194:\t\tselect ASYMMETRIC_PUBLIC_KEY_SUBTYPE\n2195:\t\tselect ASN1\n2196:\t\tselect OID_REGISTRY\n2197:\t\tselect X509_CERTIFICATE_PARSER\n2198:\t\tselect PKCS7_MESSAGE_PARSER\n2199:\t\thelp\n2200:\t\t Provide PKCS#7 message verification using the contents of the system\n2201:\t\t trusted keyring to provide public keys. This then can be used for\n2202:\t\t module verification, kexec image verification and firmware blob\n2203:\t\t verification.\n2204:\t\n2205:\tconfig PROFILING\n2206:\t\tbool \"Profiling support\"\n2207:\t\thelp\n2208:\t\t Say Y here to enable the extended profiling support mechanisms used\n2209:\t\t by profilers.\n"
} |
| 39/2 | 2026/08/28 18:32 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 56569 output: 21 thoughts: 14 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Investigating `SYSTEM_DATA_VERIFICATION` Selects** Okay, so I need to figure out what's querying or accessing `SYSTEM_DATA_VERIFICATION`. My initial thought is to trace the lineage and see what processes are touching this sensitive piece of data. I’ll need to analyze the execution plans to understand which queries are running and if this is a frequent occurrence or a single instance event. I'm thinking I'll start by querying the system catalogs, maybe looking for specific table scans or index lookups involving `SYSTEM_DATA_VERIFICATION`. I need to isolate the access patterns. Is it SELECT, INSERT, UPDATE, DELETE, or something else? Understanding the DML type is key. Once I identify the queries, I can analyze the calling procedures, stored procedures, or applications initiating these requests, and from there determine the business needs. The goal is to correlate the query behavior with specific applications or users, and determine the intent, access patterns and compliance. Is the access legitimate and expected? Are there any unexpected queries that warrant investigation, potential optimization opportunities, or access control reviews? If there's an issue, I'll need to drill down deeper to understand the logic behind that process or application, verify credentials, investigate the context of access, and address any potential vulnerabilities. |
| 40/2 | 2026/08/28 18:33 | tool | grepper |
0mArgs:{
"Expression": "select SYSTEM_DATA_VERIFICATION"
}Results: {
"Output": "arch/s390/Kconfig=75=config S390\n--\narch/s390/Kconfig-278-\tselect SYSCTL_EXCEPTION_TRACE\narch/s390/Kconfig:279:\tselect SYSTEM_DATA_VERIFICATION if KEXEC_SIG\narch/s390/Kconfig-280-\tselect THREAD_INFO_IN_TASK\n--\ndrivers/md/Kconfig=581=config DM_VERITY_VERIFY_ROOTHASH_SIG\n--\ndrivers/md/Kconfig-583-\tdepends on DM_VERITY\ndrivers/md/Kconfig:584:\tselect SYSTEM_DATA_VERIFICATION\ndrivers/md/Kconfig-585-\thelp\n--\nfs/ubifs/Kconfig=89=config UBIFS_FS_AUTHENTICATION\n--\nfs/ubifs/Kconfig-92-\tselect CRYPTO_HMAC\nfs/ubifs/Kconfig:93:\tselect SYSTEM_DATA_VERIFICATION\nfs/ubifs/Kconfig-94-\thelp\n--\nfs/verity/Kconfig=34=config FS_VERITY_BUILTIN_SIGNATURES\n--\nfs/verity/Kconfig-36-\tdepends on FS_VERITY\nfs/verity/Kconfig:37:\tselect SYSTEM_DATA_VERIFICATION\nfs/verity/Kconfig-38-\thelp\n--\ninit/Kconfig=2271=config MODULE_SIG_FORMAT\ninit/Kconfig-2272-\tdef_bool n\ninit/Kconfig:2273:\tselect SYSTEM_DATA_VERIFICATION\ninit/Kconfig-2274-\n--\nnet/wireless/Kconfig=89=config CFG80211_REQUIRE_SIGNED_REGDB\n--\nnet/wireless/Kconfig-91-\tdefault y\nnet/wireless/Kconfig:92:\tselect SYSTEM_DATA_VERIFICATION\nnet/wireless/Kconfig-93-\thelp\n--\nsecurity/ipe/Kconfig=6=menuconfig SECURITY_IPE\n--\nsecurity/ipe/Kconfig-10-\tselect PKCS7_MESSAGE_PARSER\nsecurity/ipe/Kconfig:11:\tselect SYSTEM_DATA_VERIFICATION\nsecurity/ipe/Kconfig-12-\tselect IPE_PROP_DM_VERITY if DM_VERITY\n"
} |
| 41/2 | 2026/08/28 18:33 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 57468 output: 20 thoughts: 17 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Investigating Module Signature Verification** Okay, so I'm digging into this module signature verification process, and I need to start with the foundational elements. My first instinct is to zero in on `MODULE_SIG_FORMAT` and `MODULE_SIG`. These are the crucial variables, aren't they? They're the building blocks. I need to understand exactly how the signature is *formatted* and how the signature itself is *stored* within the module data. I'm assuming that they are defined in a known header file and/or are defined during the build. Are we using the standard format or something customized? This is where I can expect to find the structure definition for the signature, including the key fields like the length, the signature algorithm, and, of course, the actual signature bytes. I need to make sure I understand the data layout so I can debug any issues with verification. I should also check where these are *used*, likely in the module loading and verification routines themselves. |
| 42/2 | 2026/08/28 18:33 | tool | grepper |
0mArgs:{
"Expression": "config MODULE_SIG\\b"
}Results: {
"Output": "kernel/module/Kconfig=241=config MODULE_SRCVERSION_ALL\n--\nkernel/module/Kconfig-251-\nkernel/module/Kconfig:252:config MODULE_SIG\nkernel/module/Kconfig-253-\tbool \"Module signature verification\"\n"
} |
| 43/2 | 2026/08/28 18:33 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 57811 output: 86 thoughts: 821 Thoughts: **Kernel BPF Signature Verification Deep Dive** Okay, let's break this down. My initial thought process focused on how `MODULE_SIG` triggers a chain: it selects `MODULE_SIG_FORMAT`, which then selects `SYSTEM_DATA_VERIFICATION` along with related settings like `KEYS`, and the asymmetric key type. I also saw that `SYSTEM_DATA_VERIFICATION`, `KEYS`, or `BPF_SYSCALL` can all be independently enabled, which is something I want to keep in mind. I went back and checked `kernel/bpf/Makefile`. Seeing `obj-$(CONFIG_BPF_SYSCALL) += keys.o` under `ifeq ($(CONFIG_KEYS),y)` confirmed the dependency between `BPF_SYSCALL` and the keys subsystem. Then, `security/keys/process_keys.c` is also built when `CONFIG_KEYS` is active. Next, I did a quick scan for related configurations. Specifically, I'm interested in `BPF_SYSCALL`, `KEYS`, and `SYSTEM_DATA_VERIFICATION`. I expanded `SYSTEM_DATA_VERIFICATION` to include related settings that might be at play, like `MODULE_SIG`, `SYSTEM_TRUSTED_KEYRING`, `ASYMMETRIC_KEY_TYPE`, etc. The critical piece here seems to be how `SYSTEM_DATA_VERIFICATION` is used. I drilled into `kernel/bpf/verifier.c` and other files. In `kernel/bpf/helpers.c`, I spotted the `#ifdef CONFIG_SYSTEM_DATA_VERIFICATION` guard for the `bpf_verify_pkcs7_signature` function. This signature verification function is then called by `bpf_prog_verify_signature` in `kernel/bpf/verifier.c`. Now, let's pinpoint the key functions (pun intended!) I need to consider for this analysis. I'm focusing on the "FocusSymbols" – functions that directly relate to this system: * `bpf_lookup_keyring` - Used to look up the `.bpf` keyring. * `bpf_keyring_enforced` * `key_register_bpf_keyring` - Registers a bpf keyring. * `lookup_user_key` - Modified to support `KEY_SPEC_BPF_KEYRING`. * `bpf_check` - the entry point to the verifier, calling signature verification. * `bpf_verify_pkcs7_signature` - The actual signature verification function. Finally, a quick sanity check to ensure these symbols are accessible. I verified that all those symbols (except `bpf_keyring_enforced` which is a variable) are *non-static* and *non-inlined* and declared in relevant include headers. So far, the plan looks right! Let's get to the code. |
| 44/2 | 2026/08/28 18:33 | tool | grepper |
0mArgs:{
"Expression": "^(struct bpf_key \\*bpf_lookup_keyring|bool bpf_keyring_enforced|void key_register_bpf_keyring|key_ref_t lookup_user_key|int bpf_check|__bpf_kfunc int bpf_verify_pkcs7_signature)\\b"
}Results: {
"Output": "include/linux/bpf.h=3052=struct bpf_log_attr;\ninclude/linux/bpf.h:3053:int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr,\ninclude/linux/bpf.h-3054-\t struct bpf_log_attr *attr_log);\n--\ninclude/linux/bpf.h=3822=struct bpf_key *bpf_lookup_system_key(u64 id);\ninclude/linux/bpf.h:3823:struct bpf_key *bpf_lookup_keyring(void);\ninclude/linux/bpf.h:3824:bool bpf_keyring_enforced(void);\ninclude/linux/bpf.h-3825-void bpf_key_put(struct bpf_key *bkey);\n--\nkernel/bpf/helpers.c=4299=__bpf_kfunc void bpf_key_put(struct bpf_key *bkey)\n--\nkernel/bpf/helpers.c-4317- */\nkernel/bpf/helpers.c:4318:__bpf_kfunc int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\nkernel/bpf/helpers.c-4319-\t\t\t const struct bpf_dynptr *sig_p,\n--\nkernel/bpf/keys.c=19=MODULE_PARM_DESC(keyring_unsealed, \"Leave the bpf keyring unsealed\");\nkernel/bpf/keys.c-20-\nkernel/bpf/keys.c:21:bool bpf_keyring_enforced(void)\nkernel/bpf/keys.c-22-{\n--\nkernel/bpf/keys.c-25-\nkernel/bpf/keys.c:26:struct bpf_key *bpf_lookup_keyring(void)\nkernel/bpf/keys.c-27-{\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21132-\nkernel/bpf/verifier.c:21133:int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\nkernel/bpf/verifier.c-21134-\t struct bpf_log_attr *attr_log)\n--\nsecurity/keys/process_keys.c=590=bool lookup_user_key_possessed(const struct key *key,\n--\nsecurity/keys/process_keys.c-604- */\nsecurity/keys/process_keys.c:605:void key_register_bpf_keyring(struct key *keyring)\nsecurity/keys/process_keys.c-606-{\n--\nsecurity/keys/process_keys.c-627- */\nsecurity/keys/process_keys.c:628:key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,\nsecurity/keys/process_keys.c-629-\t\t\t enum key_need_perm need_perm)\n"
} |
| 45/2 | 2026/08/28 18:33 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 60175 output: 227 thoughts: 422 Thoughts: **Analyzing the Function Symbols and System Configuration** Okay, so I've confirmed those symbols represent non-static, non-inlined functions. Now, I need to verify the required kernel configurations. Let's see... I've got: `BPF_SYSCALL`, `KEYS`, `SYSTEM_DATA_VERIFICATION`, `ASYMMETRIC_KEY_TYPE`, `PKCS7_MESSAGE_PARSER`, `X509_CERTIFICATE_PARSER`, `CRYPTO_SHA256`, `CRYPTO_SHA512`, `CRYPTO_MLDSA`, and `CRYPTO_RSA`. Just to be absolutely certain, let me double-check that list. Yep, it seems I had them all. Now I need to review the parameters I need for `set-results`. First, `WorthFuzzing` has to be set to `true`. This is important because it enables the functional logic for `.bpf` keyring management, specifically the `KEY_SPEC_BPF_KEYRING` lookup in `lookup_user_key`, and the verification enforcement in `bpf_prog_verify_signature`, all of which are very relevant. Next, I need to include a concise explanation in the `Reasoning` field. The `FocusSymbols` are: `bpf_lookup_keyring`, `bpf_keyring_enforced`, `key_register_bpf_keyring`, `lookup_user_key`, `bpf_check`, and `bpf_verify_pkcs7_signature`. Those symbols are crucial for pinpointing the area of interest. Finally, for `EnableConfigs`, I need to include `BPF_SYSCALL`, `KEYS`, `SYSTEM_DATA_VERIFICATION`, `ASYMMETRIC_KEY_TYPE`, `CRYPTO_MLDSA`, `CRYPTO_SHA512`, `CRYPTO_SHA256`, and `CRYPTO_RSA`. Alright, now I have all the components, so I'm ready to call `set-results`. |
| 46/2 | 2026/08/28 18:33 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_SYSCALL",
"KEYS",
"SYSTEM_DATA_VERIFICATION",
"ASYMMETRIC_KEY_TYPE",
"CRYPTO_MLDSA",
"CRYPTO_SHA512",
"CRYPTO_SHA256",
"CRYPTO_RSA"
],
"FocusSymbols": [
"bpf_lookup_keyring",
"bpf_keyring_enforced",
"key_register_bpf_keyring",
"lookup_user_key",
"bpf_check",
"bpf_verify_pkcs7_signature"
],
"Reasoning": "The patch introduces a dedicated BPF keyring (.bpf) selectable via KEY_SPEC_BPF_KEYRING (-9), new kernel logic for lookup, restriction, and enforcement during BPF program verification, and support for ML-DSA signed program verification. These changes modify core BPF program loading and key management logic reachable from userspace.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"KEYS",
"SYSTEM_DATA_VERIFICATION",
"ASYMMETRIC_KEY_TYPE",
"CRYPTO_MLDSA",
"CRYPTO_SHA512",
"CRYPTO_SHA256",
"CRYPTO_RSA"
],
"FocusSymbols": [
"bpf_lookup_keyring",
"bpf_keyring_enforced",
"key_register_bpf_keyring",
"lookup_user_key",
"bpf_check",
"bpf_verify_pkcs7_signature"
],
"Reasoning": "The patch introduces a dedicated BPF keyring (.bpf) selectable via KEY_SPEC_BPF_KEYRING (-9), new kernel logic for lookup, restriction, and enforcement during BPF program verification, and support for ML-DSA signed program verification. These changes modify core BPF program loading and key management logic reachable from userspace.",
"WorthFuzzing": true
} |
| 47/1 | 2026/08/28 18:33 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 48/2 | 2026/08/28 18:33 | agent | kmsan-evaluator |
1mModel:core Args: null Results: {
"KMSANReasoning": "The patch series adds support for the `.bpf` keyring (via the `bpf.keyring_unsealed` boot parameter, `KEY_SPEC_BPF_KEYRING` ID, and signature verifier integration) and tightens the maximum signature size check for BPF programs.\n\nAnalysis of uninitialized memory risk:\n1. No uninitialized data or struct padding is copied to userspace via `copy_to_user`, BPF maps, or syscall attributes.\n2. The newly introduced `struct bpf_key` allocation in `bpf_lookup_keyring()` explicitly initializes both of its struct fields (`key` and `has_ref`) prior to returning.\n3. No conditional branches rely on uninitialized stack or heap state.\n4. Signature verification operates on memory copied cleanly with `kvmemdup_bpfptr()`.\n\nAny potential bugs related to memory safety, pointer validation, or reference counting are standard lifetime or memory safety issues caught by KASAN/standard detectors. There are no KMSAN-specific uninitialized memory or info-leak risks that justify a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit e339e4245c3ea6b0a065a66a530e37d1313633f6
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Aug 28 18:30:36 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index e4643634a9b1b..e70fb15757d40 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -667,6 +667,22 @@ Kernel parameters
See Documentation/admin-guide/bootconfig.rst
+ bpf.keyring_unsealed=
+ [BPF] When set to 1, leave the bpf keyring unsealed
+ after initialization so that userspace can provision
+ keys. Once the keyring is restricted it becomes active
+ and can be used for BPF program signature verification.
+
+ Setting this also means that the bpf keyring is the
+ only non-system keyring a loader may select for the
+ rest of the boot: caller-supplied user/session
+ keyrings are refused with -EPERM, whether or not
+ provisioning actually completed. The system keyrings
+ stay selectable. Leaving it unset keeps the prior
+ behaviour, where a caller-supplied keyring is allowed.
+
+ See Documentation/bpf/signing.rst
+
bttv.card= [HW,V4L] bttv (bt848 + bt878 based grabber cards)
bttv.radio= Most important insmod options are available as
kernel args too.
diff --git a/Documentation/bpf/signing.rst b/Documentation/bpf/signing.rst
index e73eaaebd8b15..27fdad3df96f1 100644
--- a/Documentation/bpf/signing.rst
+++ b/Documentation/bpf/signing.rst
@@ -254,21 +254,24 @@ returned. Only after the program has fully loaded, at the next hook
(``security_bpf_prog()``), does ``BPF_SIG_VERIFIED`` carry its full meaning:
validly signed *and* fully verified.
-A more realistic admission policy than "is it signed at all": accept programs
-signed by a system keyring, accept a user-keyring signature only if the
-key/keyring it was verified against is on an explicit allowlist, and emit a
-tamper-evident record of every decision so that even denied attempts are
-auditable. (Illustrative - error checking elided.)
+A more realistic admission policy than "is it signed at all": base trust in
+the bpf keyring or an explicit allowlist of a caller-supplied staging key/
+keyring, and emit a record of every decision including denied attempts.
+(Illustrative - error checking elided.)
.. code-block:: c
- /* Serials of user keys/keyrings we additionally trust. */
+ /*
+ * Serials of caller-supplied keyrings we are willing to stage. Empty
+ * on a system that has committed to the bpf keyring, where the kernel
+ * refuses them anyway.
+ */
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, __s32); /* keyring_serial */
__type(value, __u8);
__uint(max_entries, 64);
- } trusted_user_keys SEC(".maps");
+ } staging_keys SEC(".maps");
/* Audit stream consumed by a userspace logger. */
struct {
@@ -291,11 +294,18 @@ auditable. (Illustrative - error checking elided.)
if (kernel)
return 0; /* trust in-kernel loads */
- if (verdict != BPF_SIG_VERIFIED)
+ if (verdict != BPF_SIG_VERIFIED) {
ret = -EPERM; /* must be validly signed */
- else if (ktype == BPF_SIG_KEYRING_USER &&
- !bpf_map_lookup_elem(&trusted_user_keys, &serial))
- ret = -EPERM; /* key/keyring not allowlisted */
+ } else switch (ktype) {
+ case BPF_SIG_KEYRING_BPF:
+ break;
+ case BPF_SIG_KEYRING_USER:
+ if (!bpf_map_lookup_elem(&staging_keys, &serial))
+ ret = -EPERM;
+ break;
+ default:
+ ret = -EPERM; /* keyring not in policy */
+ }
d = bpf_ringbuf_reserve(&audit, sizeof(*d), 0);
if (d) {
@@ -309,6 +319,11 @@ auditable. (Illustrative - error checking elided.)
return ret;
}
+Such a policy is what makes a caller-supplied keyring usable at all on a system
+that does not boot with ``bpf.keyring_unsealed=1``: the allowlist bounds which
+staged keys count, and the LSM itself has to protect them from being tampered
+with.
+
Observing a verified load: ``security_bpf_prog()``
--------------------------------------------------
@@ -381,8 +396,9 @@ that verdict covered all of its exclusive maps, rejecting any that did not - so
a deny-by-default admission policy needs no second enforcement point. Use
``security_bpf_prog()`` to record or finally gate the verified programs once
they carry an id. The ``verdict``, ``keyring_type`` and ``keyring_serial`` fields
-let a policy distinguish, for example, "verified and signed by a builtin key"
-from "verified by a user key". A policy LSM such as IPE could consume the same
+let a policy distinguish "verified against the operator's bpf keyring" from
+"verified against a keyring the loader supplied itself", which is the
+distinction that matters most. A policy LSM such as IPE could consume the same
hooks to enforce system policy without writing any BPF, though none implements
this today.
@@ -390,33 +406,158 @@ Keyrings
========
``keyring_id`` selects the trusted keyring the PKCS#7 signature is verified
-against. The well-known ids ``0`` (builtin), ``VERIFY_USE_SECONDARY_KEYRING``
-and ``VERIFY_USE_PLATFORM_KEYRING`` select the corresponding system keyrings;
-any other value is treated as the serial of a user/session key or keyring.
-The keyring is looked up first, before the signature bytes are examined, so a
-signature naming a non-existent keyring is rejected up front, and a failed
-verification aborts the load - so a program that loads successfully with a
-signature always has consistent keyring fields recorded.
+against. Four values are well-known; anything else is resolved as a caller-
+supplied key or keyring, named either by its serial or by one of the other
+``KEY_SPEC_*`` ids:
+
+.. list-table::
+ :header-rows: 1
+
+ * - ``keyring_id``
+ - Keyring
+ * - ``0``
+ - builtin trusted keyring
+ * - ``VERIFY_USE_SECONDARY_KEYRING`` (``1``)
+ - secondary trusted keyring
+ * - ``VERIFY_USE_PLATFORM_KEYRING`` (``2``)
+ - platform keyring
+ * - ``KEY_SPEC_BPF_KEYRING`` (``-9``)
+ - the bpf keyring
+ * - anything else
+ - a caller-supplied user/session key or keyring
+
+The keyring is resolved first, before the signature bytes are examined, so a
+signature naming a keyring that cannot be used is rejected up front, and a
+failed verification aborts the load - a program that loads successfully with
+a signature therefore always has consistent keyring fields recorded.
+
+The bpf keyring
+---------------
+
+A system keyring needs a kernel rebuild or a vouched-for enrollment to rotate a
+key, and grants BPF-signing trust to keys trusted for everything else in the
+kernel too. A caller-supplied keyring, at the other extreme, is filled by the
+very process that loads the program and so carries no trust of its own.
+
+The bpf keyring fills that gap and is the trust anchor which a signed BPF
+deployment should be built on top of: a keyring named ``.bpf``, selected with
+``KEY_SPEC_BPF_KEYRING``, that an operator provisions at boot with a key
+scoped to BPF program loading and nothing else in the kernel's trust hierarchy.
+It is owned by the operator rather than by the loader, and rotatable across a
+reboot without touching the kernel image. It is modelled after the dm-verity
+keyring (see ``dm_verity.keyring_unsealed=``) and provisioned the same way: an
+initrd runs the ``keyctl`` steps below before handing off to the rootfs.
+
+Provisioning
+~~~~~~~~~~~~
+
+The keyring is created during ``late_initcall`` and is **sealed empty** by
+default: it carries a reject-all restriction, so no key can ever be added and
+``KEY_SPEC_BPF_KEYRING`` fails with ``-ENOKEY`` for the whole boot.
+
+``bpf.keyring_unsealed=1`` leaves it unrestricted at init so the initrd can
+provision it. The keyring is not linked into any process keyring, but the
+``KEY_SPEC_BPF_KEYRING`` special key id addresses it directly, so its serial
+does not have to be looked up first. Steps would be as follows::
+
+ keyctl padd asymmetric "" -9 < signing_key.der
+ keyctl restrict_keyring -9
+
+Both steps are required: the keyring is consulted only once it is **non-empty
+and restricted**. An unrestricted keyring is ignored even when it holds keys,
+so a half-provisioned keyring is inert rather than a weaker trust anchor, and a
+load naming it fails with ``-ENOKEY`` and a verifier log. Restricting cannot
+be undone.
+
+More than one key is enrolled by repeating the ``keyctl padd`` step; the
+restriction is applied once, after the last of them::
+
+ for key in /etc/bpf/keys/*.der; do
+ keyctl padd asymmetric "" -9 < $key
+ done
+
+ keyctl restrict_keyring -9
+ keyctl show -9
+
+The restriction bounds what can be added, never what can be taken away. A key
+that is already enrolled can still be unlinked, and the keyring cleared or
+revoked, by anything running as root. That does not weaken the anchor, since
+a keyring left empty is no longer consulted and a load naming it fails with
+``-ENOKEY``, but it does take signed loading out until the next boot. Dropping
+the user permissions the keyring no longer needs would close that; as a third
+step in the initrd::
+
+ keyctl setperm -9 0x08030000
+
+What remains is ``KEY_POS_SEARCH`` for the in-kernel search during verification,
+plus ``KEY_USR_VIEW`` and ``KEY_USR_READ`` so the keyring stays visible in
+``/proc/keys``. ``KEY_SPEC_BPF_KEYRING`` names the keyring but conveys no rights
+of its own - the keyring is not possessed by anyone, so the ``KEY_POS_*`` bits
+are only ever reached by the in-kernel search, and userspace is left with what
+the ``KEY_USR_*`` bits grant. Once they are dropped, addressing it by the serial
+``/proc/keys`` reports gets no further than the special id does.
+
+Provisioning has to complete before control passes to the rootfs. The keyring
+takes any number of keys for as long as it is unrestricted, so it is the
+restriction that bounds the enrolled set, not the first enrollment. Before
+the initrd hands off control, it must therefore restrict the keyring after
+the last enrollment.
+
+Enforcement
+~~~~~~~~~~~
+
+``bpf.keyring_unsealed=1`` states that the bpf keyring is *the* trust anchor for
+this boot, so it does more than unseal. From the first program load onwards a
+caller-supplied user/session keyring is refused with ``-EPERM`` and a verifier
+log message, whether or not provisioning ever completed. The system keyrings
+stay selectable.
+
+Enforcement is readable at ``/sys/module/bpf/parameters/keyring_unsealed``, and
+read-only there: the flag is ``__ro_after_init`` behind a 0444 parameter. It is
+also derived from the boot flag rather than from the keyring's runtime state,
+so there is no window early in boot during which a caller-supplied keyring is
+still accepted.
+
+Caller-supplied keyrings are for staging
+----------------------------------------
+
+A ``keyring_id`` naming a user or session key or keyring is a *staging*
+mechanism, not a trust anchor: it is filled by the same userspace that loads the
+program, so verifying against it establishes only that the loader signed what it
+loaded. Its purpose is to let software installed onto a running system - whose
+signing key is not enrolled anywhere yet - run signed until that key reaches the
+bpf keyring on the next boot.
+
+A system that has committed to the bpf keyring refuses this path outright (see
+`Enforcement`_). A system that has not can still allow it, but a policy must
+never treat ``BPF_SIG_KEYRING_USER`` as equivalent to the bpf or system
+keyrings; it should allowlist the specific serials it is willing to stage and
+pair that with a BPF LSM policy protecting those keys from tampering, as in
+`Enforcement via LSMs`_.
+
+Recorded fields
+---------------
Two fields are recorded in ``prog->aux->sig`` for an LSM to inspect:
``keyring_type`` (``enum bpf_sig_keyring``)
Classified purely from ``keyring_id`` whenever the program is signed:
``BPF_SIG_KEYRING_BUILTIN``, ``_SECONDARY``, ``_PLATFORM`` for the system
- keyrings, or ``_USER`` for a user/session keyring. It is
- ``BPF_SIG_KEYRING_NONE`` for an unsigned program.
+ keyrings, ``_BPF`` for the bpf keyring, or ``_USER`` for a caller-supplied
+ user/session keyring. It is ``BPF_SIG_KEYRING_NONE`` for an unsigned
+ program.
``keyring_serial`` (``s32``)
Set **only** on a successful verification, to the serial of the
- **user/session key or keyring** that ``keyring_id`` resolved to - the
+ **caller-supplied key or keyring** that ``keyring_id`` resolved to - the
object the signature was verified against, not the individual asymmetric
key inside it that matched the signer. Passing
``KEY_SPEC_SESSION_KEYRING``, for example, records the session keyring's
- serial. The system keyrings are trusted as a whole and expose no serial
- here, so the serial is ``0`` for builtin, secondary and platform
- signatures, and ``0`` for unsigned programs. In other words, a non-zero
- ``keyring_serial`` is exactly "verified against the user key/keyring with
- this serial".
+ serial. The system keyrings and the bpf keyring are trusted as a whole and
+ expose no serial here, so the serial is ``0`` for them, and ``0`` for
+ unsigned programs. A non-zero ``keyring_serial`` is therefore exactly
+ "verified against the caller-supplied key/keyring with this serial", which
+ is exactly the case a policy has to scrutinise.
.. list-table::
:header-rows: 1
@@ -436,16 +577,51 @@ Two fields are recorded in ``prog->aux->sig`` for an LSM to inspect:
* - ``VERIFY_USE_PLATFORM_KEYRING``
- ``BPF_SIG_KEYRING_PLATFORM``
- ``0``
- * - other (a user/session key serial)
+ * - ``KEY_SPEC_BPF_KEYRING``
+ - ``BPF_SIG_KEYRING_BPF``
+ - ``0``
+ * - other (a caller-supplied key serial)
- ``BPF_SIG_KEYRING_USER``
- serial of the resolved key/keyring
-Producing a signed object
-==========================
+Producing and loading a signed object
+=====================================
+
+Generating a signing key
+------------------------
+
+Signing is algorithm agnostic: the algorithm comes from the X.509 certificate
+and the PKCS#7 ``SignerInfo``. Anything the X.509 and PKCS#7 parsers understand
+works with no BPF-side change. Both examples below read the certificate request
+config from ``x509.genkey``, for which ``certs/x509.genkey`` serves as a
+template (see Documentation/admin-guide/module-signing.rst). RSA::
+
+ openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \
+ -config x509.genkey -outform PEM \
+ -out signing_key.pem -keyout signing_key.pem
+ openssl x509 -in signing_key.pem -outform der -out signing_key.der
+
+ML-DSA-87 (FIPS-204), which needs openssl 3.5 or later, and both
+``CONFIG_CRYPTO_MLDSA`` and ``CONFIG_CRYPTO_SHA512`` in the kernel - the latter
+for the signedAttrs digest described below, which ``CONFIG_CRYPTO_MLDSA`` does
+not select. Note the absence of a digest option: ML-DSA hashes the message
+itself, so openssl ignores an explicit digest for it::
+
+ openssl req -new -nodes -utf8 -days 36500 -batch -x509 \
+ -newkey ML-DSA-87 -config x509.genkey -outform PEM \
+ -out signing_key.pem -keyout signing_key.pem
+ openssl x509 -in signing_key.pem -outform der -out signing_key.der
+
+``bpftool`` handles the following internally: openssl before 4.0 cannot combine
+ML-DSA with ``CMS_NOATTR``, so it falls back to signedAttrs, where only SHA-512
+is permitted. This mirrors what module signing does as well.
+
+Signing
+-------
``bpftool`` generates and signs a light skeleton in one step::
- bpftool gen skeleton -L -S -k <private_key.pem> -i <certificate.x509> \
+ bpftool gen skeleton -L -S -k signing_key.pem -i signing_key.der \
obj.bpf.o > obj.lskel.h
``-L`` selects the light-skeleton (``gen_loader``) backend and ``-S`` enables
@@ -454,12 +630,36 @@ signing; ``-k`` and ``-i`` supply the signing key and its X.509 certificate.
reconstructs - and also computes ``excl_prog_hash`` as the digest of the loader
instructions so the metadata map can be bound to the loader. The signature and
hash are embedded in the generated header; the certificate is used only for
-signing and is not included. Loading the skeleton performs the
-create/populate/freeze/load sequence described above.
+signing and is not included.
+
+Loading
+-------
+
+The generated skeleton exposes ``keyring_id``, which selects the keyring the
+kernel verifies against. Set it between open and load; loading then performs
+the create/populate/freeze/load sequence described above::
-At runtime the trusted public key must be present in the chosen keyring (for
-example added to the session keyring, or built into the kernel's builtin trusted
-keyring) for verification to succeed.
+ struct obj *skel = obj__open();
+
+ skel->keyring_id = KEY_SPEC_BPF_KEYRING;
+ err = obj__load(skel);
+
+For the staging case the same object is loaded against a keyring the caller
+populated itself, which only works on a system that has not set
+``bpf.keyring_unsealed=1``::
+
+ /*
+ * Staging only: this keyring is under the loader's own control and
+ * carries no trust of its own. See "Caller-supplied keyrings are for
+ * staging".
+ */
+ key_id = add_key("asymmetric", "", der, der_sz, KEY_SPEC_SESSION_KEYRING);
+ skel->keyring_id = KEY_SPEC_SESSION_KEYRING;
+ err = obj__load(skel);
+
+Either way the trusted public key must already be in the chosen keyring for
+verification to succeed. For the bpf keyring that enrollment happens once at
+boot, see `Provisioning`_.
UAPI reference
==============
@@ -487,6 +687,13 @@ UAPI reference
The map content is not hashed separately at all - it is covered, as bytes,
by the program signature.
+Kernel command line:
+
+``bpf.keyring_unsealed=``
+ Set to ``1`` to leave the bpf keyring unsealed for provisioning, and to make
+ it the only non-system keyring a loader may select for the rest of the boot
+ (see `The bpf keyring`_).
+
Notes and limitations
======================
@@ -495,3 +702,5 @@ Notes and limitations
exceed it.
- The metadata container is a single-element array map, accessed through
``map_direct_value_addr``.
+- The bpf keyring needs ``CONFIG_KEYS``; without it there is no bpf keyring
+ and ``KEY_SPEC_BPF_KEYRING`` never resolves.
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index c2027688be3e4..3a7eb2185c354 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1735,6 +1735,7 @@ enum bpf_sig_keyring {
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
+ BPF_SIG_KEYRING_BPF,
};
struct bpf_prog_aux {
@@ -3819,6 +3820,8 @@ struct bpf_key {
#if defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL)
struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);
struct bpf_key *bpf_lookup_system_key(u64 id);
+struct bpf_key *bpf_lookup_keyring(void);
+bool bpf_keyring_enforced(void);
void bpf_key_put(struct bpf_key *bkey);
int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,
const struct bpf_dynptr *sig_p,
@@ -3839,6 +3842,16 @@ static inline struct bpf_key *bpf_lookup_system_key(u64 id)
return NULL;
}
+static inline struct bpf_key *bpf_lookup_keyring(void)
+{
+ return NULL;
+}
+
+static inline bool bpf_keyring_enforced(void)
+{
+ return false;
+}
+
static inline void bpf_key_put(struct bpf_key *bkey)
{
}
diff --git a/include/linux/key.h b/include/linux/key.h
index 81b8f05c68985..bd10fe45819db 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -440,6 +440,8 @@ extern key_ref_t keyring_search(key_ref_t keyring,
extern int keyring_restrict(key_ref_t keyring, const char *type,
const char *restriction);
+extern void key_register_bpf_keyring(struct key *keyring);
+
extern struct key *key_lookup(key_serial_t id);
static inline key_serial_t key_serial(const struct key *key)
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 4c8884eea8084..fa85b9760391a 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -24,6 +24,7 @@
#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed request_key auth key */
#define KEY_SPEC_REQUESTOR_KEYRING -8 /* - key ID for request_key() dest keyring */
+#define KEY_SPEC_BPF_KEYRING -9 /* - key ID for the BPF-specific keyring */
/* request-key default keyrings */
#define KEY_REQKEY_DEFL_NO_CHANGE -1
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 90255d80e5be6..9a92c348bbda6 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -27,6 +27,9 @@ obj-$(CONFIG_BPF_SYSCALL) += offload.o
obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o
obj-$(CONFIG_BPF_SYSCALL) += tcx.o
endif
+ifeq ($(CONFIG_KEYS),y)
+obj-$(CONFIG_BPF_SYSCALL) += keys.o
+endif
ifeq ($(CONFIG_PERF_EVENTS),y)
obj-$(CONFIG_BPF_SYSCALL) += stackmap.o
endif
diff --git a/kernel/bpf/keys.c b/kernel/bpf/keys.c
new file mode 100644
index 0000000000000..bffc356839ac5
--- /dev/null
+++ b/kernel/bpf/keys.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2026 Isovalent */
+
+#include <linux/bpf.h>
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/key.h>
+#include <linux/moduleparam.h>
+#include <linux/slab.h>
+
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "bpf."
+
+static struct key *bpf_keyring;
+
+static bool bpf_keyring_unsealed __ro_after_init;
+module_param_named(keyring_unsealed, bpf_keyring_unsealed, bool, 0444);
+MODULE_PARM_DESC(keyring_unsealed, "Leave the bpf keyring unsealed");
+
+bool bpf_keyring_enforced(void)
+{
+ return bpf_keyring_unsealed;
+}
+
+struct bpf_key *bpf_lookup_keyring(void)
+{
+ struct bpf_key *bkey;
+
+ if (!bpf_keyring)
+ return NULL;
+ if (!READ_ONCE(bpf_keyring->keys.nr_leaves_on_tree) ||
+ !READ_ONCE(bpf_keyring->restrict_link))
+ return NULL;
+
+ bkey = kmalloc_obj(*bkey);
+ if (!bkey)
+ return NULL;
+
+ bkey->key = bpf_keyring;
+ bkey->has_ref = false;
+ return bkey;
+}
+
+static int __init bpf_keyring_init(void)
+{
+ struct key *keyring;
+
+ keyring = keyring_alloc(".bpf",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+ current_cred(), KEY_POS_SEARCH |
+ KEY_USR_VIEW | KEY_USR_READ |
+ KEY_USR_WRITE | KEY_USR_SEARCH |
+ KEY_USR_SETATTR, KEY_ALLOC_NOT_IN_QUOTA,
+ NULL, NULL);
+ if (IS_ERR(keyring)) {
+ pr_err("bpf: cannot allocate bpf keyring: %ld\n",
+ PTR_ERR(keyring));
+ return 0;
+ }
+ if (!bpf_keyring_unsealed &&
+ keyring_restrict(make_key_ref(keyring, true), NULL, NULL)) {
+ pr_err("bpf: cannot seal bpf keyring\n");
+ key_revoke(keyring);
+ key_put(keyring);
+ return 0;
+ }
+
+ bpf_keyring = keyring;
+ key_register_bpf_keyring(keyring);
+ return 0;
+}
+late_initcall(bpf_keyring_init);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e036ae20bf6b9..d50a135466bb6 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -24,6 +24,7 @@
#include <linux/bpf_lsm.h>
#include <linux/security.h>
#include <linux/verification.h>
+#include <linux/keyctl.h>
#include <linux/btf_ids.h>
#include <linux/poison.h>
#include <linux/module.h>
@@ -20972,6 +20973,14 @@ int bpf_fixup_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
return 0;
}
+/*
+ * Upper bound on the PKCS#7 signature blob passed with a program. Comfortably
+ * above the largest signature the kernel can verify, and far below anything
+ * that would make rejecting a load expensive. Deliberately a fixed number so
+ * that what the syscall accepts does not depend on PAGE_SIZE.
+ */
+#define BPF_PROG_MAX_SIGNATURE_SIZE (64 * 1024)
+
static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
{
switch (keyring_id) {
@@ -20981,6 +20990,8 @@ static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
return BPF_SIG_KEYRING_SECONDARY;
case (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:
return BPF_SIG_KEYRING_PLATFORM;
+ case KEY_SPEC_BPF_KEYRING:
+ return BPF_SIG_KEYRING_BPF;
default:
return BPF_SIG_KEYRING_USER;
}
@@ -21009,18 +21020,25 @@ static int bpf_prog_verify_signature(struct bpf_verifier_env *env,
u64 data_sz;
int err = 0;
- /*
- * Don't attempt to use kmalloc_large or vmalloc for signatures.
- * Practical signature for BPF program should be below this limit.
- */
if (!attr->signature_size ||
- attr->signature_size > KMALLOC_MAX_CACHE_SIZE)
+ attr->signature_size > BPF_PROG_MAX_SIGNATURE_SIZE)
return -EINVAL;
- if (system_keyring_id_check(attr->keyring_id) == 0)
+
+ if (system_keyring_id_check(attr->keyring_id) == 0) {
key = bpf_lookup_system_key(attr->keyring_id);
- else
+ } else if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ key = bpf_lookup_keyring();
+ } else if (bpf_keyring_enforced()) {
+ verbose(env, "caller-supplied keyring refused, use bpf keyring\n");
+ return -EPERM;
+ } else {
key = bpf_lookup_user_key(attr->keyring_id, 0);
+ }
if (!key) {
+ if (attr->keyring_id == KEY_SPEC_BPF_KEYRING) {
+ verbose(env, "the bpf keyring is empty or has not been restricted\n");
+ return -ENOKEY;
+ }
verbose(env, "cannot resolve signing keyring with keyring_id %d\n",
attr->keyring_id);
return -EINVAL;
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a63c46bb2d148..44358388e3959 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -22,6 +22,9 @@
/* Session keyring create vs join semaphore */
static DEFINE_MUTEX(key_session_mutex);
+/* BPF keyring reachable through KEY_SPEC_BPF_KEYRING */
+static struct key *bpf_keyring __ro_after_init;
+
/* The root user's tracking struct */
struct key_user root_key_user = {
.usage = REFCOUNT_INIT(3),
@@ -590,6 +593,20 @@ bool lookup_user_key_possessed(const struct key *key,
return key == match_data->raw_data;
}
+/**
+ * key_register_bpf_keyring - Publish the BPF keyring for KEY_SPEC_BPF_KEYRING
+ * @keyring: The keyring to publish
+ *
+ * Make @keyring reachable by userspace through the KEY_SPEC_BPF_KEYRING
+ * special key ID, so that provisioning it does not require scraping its
+ * serial out of /proc/keys first. Called once, from an initcall, and never
+ * undone.
+ */
+void key_register_bpf_keyring(struct key *keyring)
+{
+ bpf_keyring = keyring;
+}
+
/*
* Look up a key ID given us by userspace with a given permissions mask to get
* the key it refers to.
@@ -741,6 +758,14 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
key_ref = make_key_ref(key, 1);
break;
+ case KEY_SPEC_BPF_KEYRING:
+ key = bpf_keyring;
+ if (!key)
+ goto error;
+ __key_get(key);
+ key_ref = make_key_ref(key, 0);
+ break;
+
default:
key_ref = ERR_PTR(-EINVAL);
if (id < 1)
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 78b6e0ebb85d8..9315a1db1f7c2 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -57,7 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
})
#define ERR_MAX_LEN 1024
-#define MAX_SIG_SIZE 4096
+#define MAX_SIG_SIZE 16384
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
index 88726a6db6d0e..adbfd4ae5c021 100644
--- a/tools/bpf/bpftool/sign.c
+++ b/tools/bpf/bpftool/sign.c
@@ -130,6 +130,12 @@ __u32 register_session_key(const char *key_der_path)
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
{
+ unsigned int signer_flags = CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
+#ifdef CMS_NO_SIGNING_TIME
+ CMS_NO_SIGNING_TIME |
+#endif
+ CMS_USE_KEYID | CMS_NOATTR;
+ const EVP_MD *cms_digest = EVP_sha256();
BIO *bd_in = NULL, *bd_out = NULL;
EVP_PKEY *private_key = NULL;
CMS_ContentInfo *cms = NULL;
@@ -167,6 +173,20 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
goto cleanup;
}
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_VERSION_NUMBER < 0x40000000L
+ if (EVP_PKEY_is_a(private_key, "ML-DSA-44") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-65") ||
+ EVP_PKEY_is_a(private_key, "ML-DSA-87")) {
+ /*
+ * See workaround in 0ad9a71933e7 ("modsign: Enable ML-DSA
+ * module signing"). Kernel only accepts sha512, see also
+ * 8bbdeb7a25b4 ("pkcs7, x509: Add ML-DSA support").
+ */
+ signer_flags &= ~CMS_NOATTR;
+ cms_digest = EVP_sha512();
+ }
+#endif
+
cms = CMS_sign(NULL, NULL, NULL, NULL,
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
CMS_STREAM);
@@ -175,9 +195,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
goto cleanup;
}
- if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
- CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
- CMS_USE_KEYID | CMS_NOATTR)) {
+ if (!CMS_add1_signer(cms, x509, private_key, cms_digest, signer_flags)) {
err = -EINVAL;
goto cleanup;
}
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 5f1a3bfc0569f..05b3ee64290fa 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -663,7 +663,7 @@ $(TRUNNER_BPF_LSKELS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
}) && \
rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
-$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
+$(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) $(VERIFICATION_CERT) | $(TRUNNER_OUTPUT)
$(Q)$(if $(PERMISSIVE),if [ ! -f $$< ]; then \
$$(RM) $$@; \
printf ' %-12s %s\n' 'SKIP-SKEL' '$$(notdir $$@)' 1>&2; \
diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index ea7044f30adc3..2f79688dcf7ce 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -12,7 +12,9 @@ CONFIG_BPF_SYSCALL=y
# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set
CONFIG_CGROUP_BPF=y
CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MLDSA=y
CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_USER_API=y
CONFIG_CRYPTO_USER_API_HASH=y
CONFIG_CRYPTO_USER_API_SKCIPHER=y
diff --git a/tools/testing/selftests/bpf/prog_tests/signed_loader.c b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
index 77381d345435c..a0f93756e717b 100644
--- a/tools/testing/selftests/bpf/prog_tests/signed_loader.c
+++ b/tools/testing/selftests/bpf/prog_tests/signed_loader.c
@@ -32,11 +32,22 @@ enum {
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
+ BPF_SIG_KEYRING_BPF,
};
-static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
- const void *sig, __u32 sig_sz, __s32 keyring_id,
- __u32 fd_array_cnt)
+#ifndef KEY_SPEC_BPF_KEYRING
+#define KEY_SPEC_BPF_KEYRING -9
+#endif
+
+/* verify_sig_setup.sh exits with this when openssl cannot do ML-DSA. */
+#define SETUP_SKIP (-77)
+
+/* FIPS-204 ML-DSA-87 signature size, see include/crypto/mldsa.h. */
+#define MLDSA87_SIGNATURE_SIZE 4627
+
+static int load_loader_log(const void *insns, __u32 insns_sz, int map_fd,
+ const void *sig, __u32 sig_sz, __s32 keyring_id,
+ __u32 fd_array_cnt, char *log_buf, __u32 log_sz)
{
union bpf_attr attr;
int fd;
@@ -48,18 +59,31 @@ static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
attr.license = ptr_to_u64("Dual BSD/GPL");
attr.prog_flags = BPF_F_SLEEPABLE;
attr.fd_array = ptr_to_u64(&map_fd);
+ attr.fd_array_cnt = fd_array_cnt;
if (sig) {
attr.signature = ptr_to_u64(sig);
attr.signature_size = sig_sz;
attr.keyring_id = keyring_id;
}
- attr.fd_array_cnt = fd_array_cnt;
+ if (log_buf) {
+ attr.log_level = 1;
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_sz;
+ }
memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
offsetofend(union bpf_attr, keyring_id));
return fd < 0 ? -errno : fd;
}
+static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
+ const void *sig, __u32 sig_sz, __s32 keyring_id,
+ __u32 fd_array_cnt)
+{
+ return load_loader_log(insns, insns_sz, map_fd, sig, sig_sz, keyring_id,
+ fd_array_cnt, NULL, 0);
+}
+
static int run_gen_loader(const void *insns, __u32 insns_sz,
const void *data, __u32 data_sz,
const void *excl, __u32 excl_sz,
@@ -156,12 +180,30 @@ static int run_setup(const char *cmd, const char *dir)
}
if (waitpid(pid, &status, 0) < 0)
return -errno;
- return (WIFEXITED(status) &&
- WEXITSTATUS(status) == 0) ? 0 : -EINVAL;
+ if (!WIFEXITED(status))
+ return -EINVAL;
+ return -WEXITSTATUS(status);
}
-static int sign_buf(const char *dir, const void *buf, __u32 len,
- void *sig, __u32 *sig_sz)
+static void genkey_dir_fini(const char *dir)
+{
+ static const char * const files[] = {
+ "signing_key.der", "signing_key.pem", "x509.genkey",
+ };
+ char path[PATH_MAX];
+ size_t i;
+
+ if (!dir)
+ return;
+ for (i = 0; i < ARRAY_SIZE(files); i++) {
+ snprintf(path, sizeof(path), "%s/%s", dir, files[i]);
+ unlink(path);
+ }
+ rmdir(dir);
+}
+
+static int sign_buf_digest(const char *dir, const void *buf, __u32 len,
+ void *sig, __u32 *sig_sz, const char *digest)
{
char data_tmpl[PATH_MAX], key[PATH_MAX];
char sigpath[PATH_MAX + sizeof(".p7s")];
@@ -176,6 +218,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
fd = mkstemp(data_tmpl);
if (fd < 0)
return -errno;
+ snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
if (write(fd, buf, len) != (ssize_t)len) {
close(fd);
ret = -EIO;
@@ -190,7 +233,7 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
}
if (pid == 0) {
snprintf(key, sizeof(key), "%s/signing_key.pem", dir);
- execlp("./sign-file", "./sign-file", "-d", "sha256",
+ execlp("./sign-file", "./sign-file", "-d", digest,
key, key, data_tmpl, NULL);
exit(1);
}
@@ -200,34 +243,38 @@ static int sign_buf(const char *dir, const void *buf, __u32 len,
goto out;
}
- snprintf(sigpath, sizeof(sigpath), "%s.p7s", data_tmpl);
if (stat(sigpath, &st) < 0) {
ret = -errno;
goto out;
}
if (st.st_size > (off_t)*sig_sz) {
ret = -E2BIG;
- goto out_sig;
+ goto out;
}
fd = open(sigpath, O_RDONLY);
if (fd < 0) {
ret = -errno;
- goto out_sig;
+ goto out;
}
if (read(fd, sig, st.st_size) != st.st_size) {
close(fd);
ret = -EIO;
- goto out_sig;
+ goto out;
}
close(fd);
*sig_sz = st.st_size;
-out_sig:
- unlink(sigpath);
out:
+ unlink(sigpath);
unlink(data_tmpl);
return ret;
}
+static int sign_buf(const char *dir, const void *buf, __u32 len,
+ void *sig, __u32 *sig_sz)
+{
+ return sign_buf_digest(dir, buf, len, sig, sig_sz, "sha256");
+}
+
struct gen_loader_fixture {
struct test_signed_loader *skel;
struct gen_loader_opts gopts;
@@ -457,7 +504,7 @@ static void signed_btf_fd_array_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -529,7 +576,6 @@ static void signature_failure_logs(void)
static const __u8 junk[64] = { 0x30, 0x42, 0x13, 0x37, };
char log_buf[1024] = {};
struct gen_loader_fixture f;
- union bpf_attr attr;
int fd;
if (gen_loader_fixture_init(&f) == 0) {
@@ -538,22 +584,9 @@ static void signature_failure_logs(void)
* failure is reported through the verifier log. A present-but-
* invalid signature is rejected and the log says why.
*/
- memset(&attr, 0, sizeof(attr));
- attr.prog_type = BPF_PROG_TYPE_SYSCALL;
- attr.insns = ptr_to_u64(f.gopts.insns);
- attr.insn_cnt = f.gopts.insns_sz / sizeof(struct bpf_insn);
- attr.license = ptr_to_u64("Dual BSD/GPL");
- attr.prog_flags = BPF_F_SLEEPABLE;
- attr.signature = ptr_to_u64(junk);
- attr.signature_size = sizeof(junk);
- attr.keyring_id = KEY_SPEC_SESSION_KEYRING;
- attr.log_level = 1;
- attr.log_buf = ptr_to_u64(log_buf);
- attr.log_size = sizeof(log_buf);
- memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
-
- fd = syscall(__NR_bpf, BPF_PROG_LOAD, &attr,
- offsetofend(union bpf_attr, keyring_id));
+ fd = load_loader_log(f.gopts.insns, f.gopts.insns_sz, -1, junk,
+ sizeof(junk), KEY_SPEC_SESSION_KEYRING, 0,
+ log_buf, sizeof(log_buf));
ASSERT_LT(fd, 0, "invalid signature rejected at load");
if (fd >= 0)
close(fd);
@@ -571,8 +604,9 @@ static void signature_too_large(void)
if (gen_loader_fixture_init(&f) == 0) {
/*
- * signature_size beyond the kernel's bound (KMALLOC_MAX_CACHE_SIZE)
- * is rejected before the buffer is read.
+ * signature_size beyond the kernel's bound
+ * (BPF_PROG_MAX_SIGNATURE_SIZE) is rejected before the buffer
+ * is read.
*/
fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
64 << 20, KEY_SPEC_SESSION_KEYRING, 0);
@@ -626,6 +660,280 @@ static void signature_bad_keyring(void)
gen_loader_fixture_fini(&f);
}
+static bool keyring_unsealed_boot(void)
+{
+ char val = 0;
+ int fd;
+
+ fd = open("/sys/module/bpf/parameters/keyring_unsealed", O_RDONLY);
+ if (fd < 0)
+ return false;
+ if (read(fd, &val, 1) != 1)
+ val = 0;
+ close(fd);
+ return val == 'Y' || val == '1';
+}
+
+static int bpf_keyring_lookup(int *nr_keys)
+{
+ char line[512], type[32], desc[64];
+ int serial = -ENOENT;
+ FILE *f;
+
+ f = fopen("/proc/keys", "r");
+ if (!f)
+ return -errno;
+
+ while (fgets(line, sizeof(line), f)) {
+ unsigned int hex;
+ char *sum;
+
+ if (sscanf(line, "%x %*s %*s %*s %*s %*s %*s %31s %63s",
+ &hex, type, desc) != 3)
+ continue;
+ if (strcmp(type, "keyring") || strcmp(desc, ".bpf:"))
+ continue;
+
+ serial = (int)hex;
+ if (nr_keys) {
+ sum = strstr(line, ".bpf: ");
+ *nr_keys = !sum || !strncmp(sum + 6, "empty", 5) ?
+ 0 : atoi(sum + 6);
+ }
+ break;
+ }
+ fclose(f);
+ return serial;
+}
+
+static long keyctl_ret(int cmd, unsigned long arg2, unsigned long arg3)
+{
+ long ret = syscall(__NR_keyctl, cmd, arg2, arg3);
+
+ return ret < 0 ? -errno : ret;
+}
+
+/*
+ * What the bpf keyring still needs once it got provisioned: KEY_POS_SEARCH
+ * for the in-kernel search during verification, and the user view/read bits
+ * so it stays visible in /proc/keys, rest is dropped so the enrolled is
+ * therefore final.
+ */
+#define BPF_KEYRING_PERM_LOCKED 0x08030000
+/* What bpf_keyring_init() grants at boot. */
+#define BPF_KEYRING_PERM_INITIAL 0x082f0000
+
+static void bpf_keyring_sealed(void)
+{
+ static const __u8 junk[64] = {};
+ struct gen_loader_fixture f;
+ int serial, key, fd;
+
+ if (keyring_unsealed_boot()) {
+ printf("%s:SKIP:the bpf keyring was unsealed at boot\n", __func__);
+ test__skip();
+ return;
+ }
+ serial = bpf_keyring_lookup(NULL);
+ if (serial >= 0) {
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID,
+ KEY_SPEC_BPF_KEYRING, 0), serial,
+ "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring");
+ key = syscall(__NR_add_key, "user", "sealprobe", "x", 1,
+ KEY_SPEC_BPF_KEYRING);
+ if (key >= 0)
+ syscall(__NR_keyctl, KEYCTL_UNLINK, key,
+ KEY_SPEC_BPF_KEYRING);
+ ASSERT_EQ(key < 0 ? -errno : 0, -EPERM,
+ "nothing links into a sealed keyring");
+ }
+ if (gen_loader_fixture_init(&f) == 0) {
+ fd = load_loader(f.gopts.insns, f.gopts.insns_sz, -1, junk,
+ sizeof(junk), KEY_SPEC_BPF_KEYRING, 0);
+ ASSERT_EQ(fd, -ENOKEY, "sealed bpf keyring rejected");
+ if (fd >= 0)
+ close(fd);
+ }
+ gen_loader_fixture_fini(&f);
+}
+
+static int try_load(const struct gen_loader_fixture *f, const void *sig,
+ __u32 sig_sz, __s32 keyring_id, char *log_buf, __u32 log_sz)
+{
+ int map_fd, prog_fd;
+
+ map_fd = setup_meta_map(f);
+ if (!ASSERT_OK_FD(map_fd, "meta_map"))
+ return map_fd;
+ prog_fd = load_loader_log(f->gopts.insns, f->gopts.insns_sz, map_fd,
+ sig, sig_sz, keyring_id, 1, log_buf, log_sz);
+ close(map_fd);
+ if (prog_fd >= 0)
+ close(prog_fd);
+ return prog_fd;
+}
+
+/*
+ * This needs bpf.keyring_unsealed=1 on the guest kernel command line, which
+ * vmtest.sh can pass via KERNEL_CMDLINE_EXTRA. There is no way to unseal the
+ * keyring from here, so without it the test skips. It also only works once
+ * per boot, as restricting a keyring cannot be undone.
+ */
+static void bpf_keyring_provisioned(void)
+{
+ char dir_tmpl[] = "/tmp/bpfkeyringXXXXXX";
+ char bad_tmpl[] = "/tmp/bpfkeyringbadXXXXXX";
+ __u8 *sig = NULL, *bad = NULL, *buf = NULL;
+ int serial, err;
+ int nr_keys = 0, der_fd = -1;
+ struct gen_loader_fixture f;
+ __u32 sig_sz = 8192, bad_sz;
+ bool have_fixture = false;
+ char *dir, *bad_dir = NULL;
+ char log_buf[1024] = {};
+ char path[PATH_MAX];
+ __u8 der[4096];
+ ssize_t der_sz;
+
+ serial = bpf_keyring_lookup(&nr_keys);
+ if (serial < 0) {
+ printf("%s:SKIP:no bpf keyring (needs CONFIG_KEYS)\n", __func__);
+ test__skip();
+ return;
+ }
+ if (nr_keys != 0) {
+ printf("%s:SKIP:the bpf keyring has already been provisioned\n",
+ __func__);
+ test__skip();
+ return;
+ }
+
+ dir = mkdtemp(dir_tmpl);
+ if (!ASSERT_OK_PTR(dir, "mkdtemp"))
+ return;
+ if (!ASSERT_OK(run_setup("genkey", dir), "verify_sig_setup genkey"))
+ goto rmdir;
+
+ snprintf(path, sizeof(path), "%s/signing_key.der", dir);
+ der_fd = open(path, O_RDONLY);
+ if (!ASSERT_OK_FD(der_fd, "open signing_key.der"))
+ goto rmdir;
+ der_sz = read(der_fd, der, sizeof(der));
+ close(der_fd);
+ if (!ASSERT_GT(der_sz, 0, "read signing_key.der"))
+ goto rmdir;
+
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),
+ serial, "KEY_SPEC_BPF_KEYRING resolves to the bpf keyring");
+
+ err = syscall(__NR_add_key, "asymmetric", "", der, (size_t)der_sz,
+ KEY_SPEC_BPF_KEYRING);
+ if (err < 0 && errno == EPERM) {
+ printf("%s:SKIP:the bpf keyring is sealed, need bpf.keyring_unsealed=1\n",
+ __func__);
+ test__skip();
+ goto rmdir;
+ }
+ if (!ASSERT_GE(err, 0, "add the signing key to the bpf keyring"))
+ goto rmdir;
+
+ sig = malloc(sig_sz);
+ if (!ASSERT_OK_PTR(sig, "sig buf"))
+ goto out;
+ have_fixture = true;
+ if (gen_loader_fixture_init(&f) != 0)
+ goto out;
+
+ buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
+ if (!ASSERT_OK_PTR(buf, "signbuf"))
+ goto out;
+ memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
+ memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
+ if (!ASSERT_OK(sign_buf(dir, buf, f.gopts.insns_sz + f.data_sz, sig,
+ &sig_sz), "sign insns||metadata"))
+ goto out;
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),
+ -ENOKEY, "unrestricted keyring still not consulted");
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),
+ -EPERM, "caller-supplied keyring refused before provisioning");
+
+ if (!ASSERT_OK(syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING,
+ KEY_SPEC_BPF_KEYRING, NULL, NULL),
+ "restrict bpf keyring"))
+ goto out;
+
+ if (!ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING,
+ NULL, 0),
+ "load signed by a key in the .bpf keyring"))
+ goto out;
+
+ bad_dir = mkdtemp(bad_tmpl);
+ if (!ASSERT_OK_PTR(bad_dir, "mkdtemp unenrolled"))
+ goto out;
+ if (!ASSERT_OK(run_setup("genkey", bad_dir), "verify_sig_setup genkey unenrolled"))
+ goto out;
+ bad_sz = 8192;
+ bad = malloc(bad_sz);
+ if (!ASSERT_OK_PTR(bad, "bad sig buf"))
+ goto out;
+ if (!ASSERT_OK(sign_buf(bad_dir, buf, f.gopts.insns_sz + f.data_sz, bad,
+ &bad_sz), "sign with an unenrolled key"))
+ goto out;
+
+ ASSERT_EQ(try_load(&f, bad, bad_sz, KEY_SPEC_BPF_KEYRING, log_buf,
+ sizeof(log_buf)), -ENOKEY,
+ "key outside the bpf keyring refused");
+ ASSERT_HAS_SUBSTR(log_buf, "signature verification failed",
+ "the bpf keyring was consulted");
+
+ f.blob[0] ^= 0xff;
+ err = try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0);
+ f.blob[0] ^= 0xff;
+ ASSERT_EQ(err, -EKEYREJECTED, "tampered metadata refused");
+
+ ASSERT_EQ(try_load(&f, sig, sig_sz, KEY_SPEC_SESSION_KEYRING, NULL, 0),
+ -EPERM, "caller-supplied keyring refused once .bpf is in use");
+
+ err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);
+ ASSERT_EQ(err, -ENOENT, "keyring writable while the user bits are there");
+
+ err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_LOCKED);
+ if (!ASSERT_OK(err, "drop the user bits on the bpf keyring"))
+ goto out;
+
+ ASSERT_OK_FD(try_load(&f, sig, sig_sz, KEY_SPEC_BPF_KEYRING, NULL, 0),
+ "load still verified against the locked keyring");
+ ASSERT_EQ(keyctl_ret(KEYCTL_GET_KEYRING_ID, KEY_SPEC_BPF_KEYRING, 0),
+ -EACCES, "the special id grants no rights of its own");
+
+ err = keyctl_ret(KEYCTL_UNLINK, KEY_SPEC_SESSION_KEYRING, serial);
+ ASSERT_EQ(err, -EACCES, "unlink refused");
+ err = keyctl_ret(KEYCTL_CLEAR, serial, 0);
+ ASSERT_EQ(err, -EACCES, "clear refused");
+ err = keyctl_ret(KEYCTL_REVOKE, serial, 0);
+ ASSERT_EQ(err, -EACCES, "revoke refused");
+ err = keyctl_ret(KEYCTL_INVALIDATE, serial, 0);
+ ASSERT_EQ(err, -EACCES, "invalidate refused");
+ err = keyctl_ret(KEYCTL_SET_TIMEOUT, serial, 1);
+ ASSERT_EQ(err, -EACCES, "timeout refused");
+ err = keyctl_ret(KEYCTL_SETPERM, serial, BPF_KEYRING_PERM_INITIAL);
+ ASSERT_EQ(err, -EACCES, "the bits cannot be granted back");
+
+ ASSERT_EQ(bpf_keyring_lookup(&nr_keys), serial, "keyring still there");
+ ASSERT_EQ(nr_keys, 1, "the enrolled key survived");
+out:
+ if (have_fixture)
+ gen_loader_fixture_fini(&f);
+ genkey_dir_fini(bad_dir);
+ free(buf);
+ free(bad);
+ free(sig);
+rmdir:
+ genkey_dir_fini(dir);
+}
+
/*
* A signed loader must ignore ctx-supplied map dimensions: the host cannot
* resize a signed program's maps via the loader ctx. Drive a one-map program
@@ -831,7 +1139,7 @@ static void signature_authenticates_insns(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -931,7 +1239,7 @@ static void signature_authenticates_metadata(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1267,7 +1575,7 @@ static void lsm_signature_verdict(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
dir = NULL;
goto out;
@@ -1450,7 +1758,7 @@ static void loadtime_verify(struct bpf_object *obj, int expect_maps)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1524,6 +1832,89 @@ static void loadtime_with_map(void)
test_signed_loader_map__destroy(skel);
}
+/*
+ * End-to-end signed load with a post-quantum key. ML-DSA (FIPS-204) is wired
+ * through the X.509 and PKCS#7 parsers, and BPF reaches them via
+ * verify_pkcs7_signature() without knowing the algorithm, so an ML-DSA key in
+ * the keyring should verify an ML-DSA signed program with no BPF-side work.
+ */
+static void mldsa_signed_load(void)
+{
+ char dir_tmpl[] = "/tmp/bpfmldsaXXXXXX";
+ int map_fd = -1, prog_fd = -1, err;
+ __u8 *sig = NULL, *buf = NULL;
+ struct gen_loader_fixture f;
+ bool have_fixture = false;
+ __u32 sig_sz = 16384;
+ char *dir;
+
+ syscall(__NR_request_key, "keyring", "_uid.0", NULL,
+ KEY_SPEC_SESSION_KEYRING);
+ dir = mkdtemp(dir_tmpl);
+ if (!ASSERT_OK_PTR(dir, "mkdtemp"))
+ return;
+
+ err = run_setup("setup-mldsa", dir);
+ if (err == SETUP_SKIP) {
+ printf("%s:SKIP:no working ML-DSA signing, set SELFTESTS_VERBOSE=1\n",
+ __func__);
+ test__skip();
+ genkey_dir_fini(dir);
+ return;
+ }
+ if (!ASSERT_OK(err, "verify_sig_setup setup-mldsa")) {
+ genkey_dir_fini(dir);
+ return;
+ }
+
+ sig = malloc(sig_sz);
+ if (!ASSERT_OK_PTR(sig, "sig buf"))
+ goto out;
+ have_fixture = true;
+ if (gen_loader_fixture_init(&f) != 0)
+ goto out;
+
+ buf = malloc((size_t)f.gopts.insns_sz + f.data_sz);
+ if (!ASSERT_OK_PTR(buf, "signbuf"))
+ goto out;
+ memcpy(buf, f.gopts.insns, f.gopts.insns_sz);
+ memcpy(buf + f.gopts.insns_sz, f.blob, f.data_sz);
+
+ /*
+ * ML-DSA hashes the message itself, but openssl before 4.0 cannot
+ * produce a CMS message without signedAttrs for it, and with those in
+ * play only SHA-512 is permitted for the messageDigest attribute.
+ */
+ if (!ASSERT_OK(sign_buf_digest(dir, buf, f.gopts.insns_sz + f.data_sz,
+ sig, &sig_sz, "sha512"),
+ "sign insns||metadata with ML-DSA"))
+ goto out;
+
+ /*
+ * Guard against the setup silently handing back some other key type:
+ * an RSA or ECDSA signature is a few hundred bytes, where an ML-DSA-87
+ * one cannot be smaller than the raw signature it carries.
+ */
+ ASSERT_GT(sig_sz, MLDSA87_SIGNATURE_SIZE, "ML-DSA-87 signature size");
+
+ map_fd = setup_meta_map(&f);
+ if (!ASSERT_OK_FD(map_fd, "meta_map"))
+ goto out;
+ prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
+ sig_sz, KEY_SPEC_SESSION_KEYRING, 1);
+ ASSERT_OK_FD(prog_fd, "ML-DSA signed loader load");
+out:
+ if (prog_fd >= 0)
+ close(prog_fd);
+ if (map_fd >= 0)
+ close(map_fd);
+ if (have_fixture)
+ gen_loader_fixture_fini(&f);
+ free(buf);
+ free(sig);
+ run_setup("cleanup", dir);
+}
+
/*
* A signed program need not bind any map. A plain BPF_PROG_TYPE_SYSCALL
* program with no fd_array is signed over its instructions alone: the kernel
@@ -1548,7 +1939,7 @@ static void signed_no_fd_array(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1619,7 +2010,7 @@ static void signed_map_by_fd_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out_map;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
goto out_map;
}
@@ -1681,7 +2072,7 @@ static void signed_sparse_fd_array_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out_map;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
goto out_map;
}
@@ -1735,7 +2126,7 @@ static void signed_module_kfunc_rejected(void)
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
return;
- if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
+ if (!ASSERT_OK(run_setup("setup-rsa", dir), "verify_sig_setup")) {
rmdir(dir);
return;
}
@@ -1777,64 +2168,71 @@ static void signed_module_kfunc_rejected(void)
run_setup("cleanup", dir);
}
+enum subtest_boot {
+ BOOT_ANY,
+ BOOT_SEALED,
+ BOOT_UNSEALED,
+};
+
+static const struct {
+ const char *name;
+ void (*fn)(void);
+ enum subtest_boot boot;
+} subtests[] = {
+ { "loadtime_no_map", loadtime_no_map, BOOT_SEALED },
+ { "loadtime_with_map", loadtime_with_map, BOOT_SEALED },
+ { "metadata_match", metadata_match, BOOT_ANY },
+ { "signature_enforced", signature_enforced, BOOT_SEALED },
+ { "signed_nonexcl_fd_array_rejected", signed_nonexcl_fd_array_rejected, BOOT_SEALED },
+ { "signed_unfrozen_fd_array_rejected", signed_unfrozen_fd_array_rejected, BOOT_SEALED },
+ { "signed_nonarray_fd_array_rejected", signed_nonarray_fd_array_rejected, BOOT_SEALED },
+ { "signed_btf_fd_array_rejected", signed_btf_fd_array_rejected, BOOT_ANY },
+ { "signed_module_kfunc_rejected", signed_module_kfunc_rejected, BOOT_SEALED },
+ { "signature_failure_logs", signature_failure_logs, BOOT_SEALED },
+ { "signature_too_large", signature_too_large, BOOT_ANY },
+ { "signature_zero_size", signature_zero_size, BOOT_ANY },
+ { "signature_bad_keyring", signature_bad_keyring, BOOT_SEALED },
+ { "bpf_keyring_sealed", bpf_keyring_sealed, BOOT_ANY },
+ { "mldsa_signed_load", mldsa_signed_load, BOOT_SEALED },
+ { "metadata_ctx_max_entries_ignored", metadata_ctx_max_entries_ignored, BOOT_ANY },
+ { "metadata_ctx_initial_value_ignored", metadata_ctx_initial_value_ignored, BOOT_ANY },
+ { "signature_authenticates_insns", signature_authenticates_insns, BOOT_SEALED },
+ { "signature_authenticates_metadata", signature_authenticates_metadata, BOOT_SEALED },
+ { "hash_requires_frozen", hash_requires_frozen, BOOT_ANY },
+ { "no_update_after_freeze", no_update_after_freeze, BOOT_ANY },
+ { "freeze_writable_mmap", freeze_writable_mmap, BOOT_ANY },
+ { "no_writable_mmap_frozen", no_writable_mmap_frozen, BOOT_ANY },
+ { "map_hash_matches_libbpf", map_hash_matches_libbpf, BOOT_ANY },
+ { "map_hash_multi_element", map_hash_multi_element, BOOT_ANY },
+ { "map_hash_bad_size", map_hash_bad_size, BOOT_ANY },
+ { "map_hash_unsupported_type", map_hash_unsupported_type, BOOT_ANY },
+ { "lsm_signature_verdict", lsm_signature_verdict, BOOT_SEALED },
+ { "signed_no_fd_array", signed_no_fd_array, BOOT_SEALED },
+ { "signed_map_by_fd_rejected", signed_map_by_fd_rejected, BOOT_SEALED },
+ { "signed_sparse_fd_array_rejected", signed_sparse_fd_array_rejected, BOOT_SEALED },
+ { "bpf_keyring_provisioned", bpf_keyring_provisioned, BOOT_UNSEALED },
+};
+
void test_signed_loader(void)
{
- if (test__start_subtest("loadtime_no_map"))
- loadtime_no_map();
- if (test__start_subtest("loadtime_with_map"))
- loadtime_with_map();
- if (test__start_subtest("metadata_match"))
- metadata_match();
- if (test__start_subtest("signature_enforced"))
- signature_enforced();
- if (test__start_subtest("signed_nonexcl_fd_array_rejected"))
- signed_nonexcl_fd_array_rejected();
- if (test__start_subtest("signed_unfrozen_fd_array_rejected"))
- signed_unfrozen_fd_array_rejected();
- if (test__start_subtest("signed_nonarray_fd_array_rejected"))
- signed_nonarray_fd_array_rejected();
- if (test__start_subtest("signed_btf_fd_array_rejected"))
- signed_btf_fd_array_rejected();
- if (test__start_subtest("signed_module_kfunc_rejected"))
- signed_module_kfunc_rejected();
- if (test__start_subtest("signature_failure_logs"))
- signature_failure_logs();
- if (test__start_subtest("signature_too_large"))
- signature_too_large();
- if (test__start_subtest("signature_zero_size"))
- signature_zero_size();
- if (test__start_subtest("signature_bad_keyring"))
- signature_bad_keyring();
- if (test__start_subtest("metadata_ctx_max_entries_ignored"))
- metadata_ctx_max_entries_ignored();
- if (test__start_subtest("metadata_ctx_initial_value_ignored"))
- metadata_ctx_initial_value_ignored();
- if (test__start_subtest("signature_authenticates_insns"))
- signature_authenticates_insns();
- if (test__start_subtest("signature_authenticates_metadata"))
- signature_authenticates_metadata();
- if (test__start_subtest("hash_requires_frozen"))
- hash_requires_frozen();
- if (test__start_subtest("no_update_after_freeze"))
- no_update_after_freeze();
- if (test__start_subtest("freeze_writable_mmap"))
- freeze_writable_mmap();
- if (test__start_subtest("no_writable_mmap_frozen"))
- no_writable_mmap_frozen();
- if (test__start_subtest("map_hash_matches_libbpf"))
- map_hash_matches_libbpf();
- if (test__start_subtest("map_hash_multi_element"))
- map_hash_multi_element();
- if (test__start_subtest("map_hash_bad_size"))
- map_hash_bad_size();
- if (test__start_subtest("map_hash_unsupported_type"))
- map_hash_unsupported_type();
- if (test__start_subtest("lsm_signature_verdict"))
- lsm_signature_verdict();
- if (test__start_subtest("signed_no_fd_array"))
- signed_no_fd_array();
- if (test__start_subtest("signed_map_by_fd_rejected"))
- signed_map_by_fd_rejected();
- if (test__start_subtest("signed_sparse_fd_array_rejected"))
- signed_sparse_fd_array_rejected();
+ bool unsealed = keyring_unsealed_boot();
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(subtests); i++) {
+ if (!test__start_subtest(subtests[i].name))
+ continue;
+ if (subtests[i].boot == BOOT_SEALED && unsealed) {
+ printf("%s:SKIP:needs a boot without bpf.keyring_unsealed=1\n",
+ subtests[i].name);
+ test__skip();
+ continue;
+ }
+ if (subtests[i].boot == BOOT_UNSEALED && !unsealed) {
+ printf("%s:SKIP:needs bpf.keyring_unsealed=1\n",
+ subtests[i].name);
+ test__skip();
+ continue;
+ }
+ subtests[i].fn();
+ }
}
diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
index f327feb8e38c3..12b146d205d75 100644
--- a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
@@ -257,7 +257,7 @@ static void test_verify_pkcs7_sig_from_map(void)
if (!ASSERT_OK_PTR(tmp_dir, "mkdtemp"))
return;
- ret = _run_setup_process(tmp_dir, "setup");
+ ret = _run_setup_process(tmp_dir, "setup-rsa");
if (!ASSERT_OK(ret, "_run_setup_process"))
goto close_prog;
@@ -458,7 +458,7 @@ static void test_pkcs7_sig_fsverity(void)
snprintf(data_path, PATH_MAX, "%s/data-file", tmp_dir);
snprintf(sig_path, PATH_MAX, "%s/sig-file", tmp_dir);
- ret = _run_setup_process(tmp_dir, "setup");
+ ret = _run_setup_process(tmp_dir, "setup-rsa");
if (!ASSERT_OK(ret, "_run_setup_process"))
goto out;
diff --git a/tools/testing/selftests/bpf/verify_sig_setup.sh b/tools/testing/selftests/bpf/verify_sig_setup.sh
index 09179fb551f09..ba5921e4b12d2 100755
--- a/tools/testing/selftests/bpf/verify_sig_setup.sh
+++ b/tools/testing/selftests/bpf/verify_sig_setup.sh
@@ -28,7 +28,7 @@ authorityKeyIdentifier=keyid
usage()
{
- echo "Usage: $0 <setup|cleanup <existing_tmp_dir>"
+ echo "Usage: $0 <setup-rsa|setup-mldsa|cleanup <existing_tmp_dir>"
exit 1
}
@@ -47,7 +47,7 @@ genkey()
${tmp_dir}/signing_key.der -outform der
}
-setup()
+setup_rsa()
{
local tmp_dir="$1"
@@ -57,6 +57,57 @@ setup()
keyctl link $key_id $keyring_id
}
+mldsa_supported()
+{
+ local tmp_dir="$1"
+
+ genkey_mldsa "${tmp_dir}" || return 1
+ : > ${tmp_dir}/probe
+ # Same digest as the caller signs with, see sign_buf_digest().
+ ./sign-file -d sha512 ${tmp_dir}/signing_key.pem \
+ ${tmp_dir}/signing_key.pem ${tmp_dir}/probe || return 1
+ rm -f ${tmp_dir}/probe ${tmp_dir}/probe.p7s
+}
+
+genkey_mldsa()
+{
+ local tmp_dir="$1"
+
+ echo "${x509_genkey_content}" > ${tmp_dir}/x509.genkey
+
+ # No -<digest> here: ML-DSA hashes the message itself, so openssl
+ # ignores an explicit digest for it.
+ openssl req -new -nodes -utf8 -days 36500 \
+ -batch -x509 -newkey ML-DSA-87 \
+ -config ${tmp_dir}/x509.genkey \
+ -outform PEM -out ${tmp_dir}/signing_key.pem \
+ -keyout ${tmp_dir}/signing_key.pem 2>&1
+
+ openssl x509 -in ${tmp_dir}/signing_key.pem -out \
+ ${tmp_dir}/signing_key.der -outform der
+}
+
+mldsa_skip()
+{
+ local tmp_dir="$1"
+
+ rm -f ${tmp_dir}/x509.genkey ${tmp_dir}/signing_key.pem \
+ ${tmp_dir}/signing_key.der ${tmp_dir}/probe \
+ ${tmp_dir}/probe.p7s
+ exit 77
+}
+
+setup_mldsa()
+{
+ local tmp_dir="$1"
+
+ mldsa_supported "${tmp_dir}" || mldsa_skip "${tmp_dir}"
+ key_id=$(cat ${tmp_dir}/signing_key.der |
+ keyctl padd asymmetric ebpf_testing_key @s)
+ keyring_id=$(keyctl newring ebpf_testing_keyring @s)
+ keyctl link $key_id $keyring_id
+}
+
cleanup() {
local tmp_dir="$1"
@@ -91,7 +142,7 @@ catch()
local exit_code="$1"
local log_file="$2"
- if [[ "${exit_code}" -ne 0 ]]; then
+ if [[ "${exit_code}" -ne 0 && "${exit_code}" -ne 77 ]]; then
cat "${log_file}" >&3
fi
@@ -108,8 +159,10 @@ main()
[[ ! -d "${tmp_dir}" ]] && echo "Directory ${tmp_dir} doesn't exist" && exit 1
- if [[ "${action}" == "setup" ]]; then
- setup "${tmp_dir}"
+ if [[ "${action}" == "setup-rsa" ]]; then
+ setup_rsa "${tmp_dir}"
+ elif [[ "${action}" == "setup-mldsa" ]]; then
+ setup_mldsa "${tmp_dir}"
elif [[ "${action}" == "genkey" ]]; then
genkey "${tmp_dir}"
elif [[ "${action}" == "cleanup" ]]; then
diff --git a/tools/testing/selftests/bpf/vmtest.sh b/tools/testing/selftests/bpf/vmtest.sh
index 6a3d026d76bd6..e7e0b419a0b87 100755
--- a/tools/testing/selftests/bpf/vmtest.sh
+++ b/tools/testing/selftests/bpf/vmtest.sh
@@ -107,6 +107,14 @@ Options:
-s) Instead of powering off the VM, start an interactive
shell. If <command> is specified, the shell runs after
the command finishes executing
+
+Environment variables:
+
+ KERNEL_CMDLINE_EXTRA
+ Extra arguments to append to the guest kernel command
+ line, for tests that need a boot-time setting. e.g:
+
+ KERNEL_CMDLINE_EXTRA="bpf.keyring_unsealed=1" $0 -- ./test_progs -t signed_loader
EOF
}
@@ -286,6 +294,12 @@ EOF
QEMU_FLAGS=("${HOST_FLAGS[@]}")
fi
+ local kernel_cmdline="root=/dev/vda rw console=${QEMU_CONSOLE}"
+
+ if [[ -n "${KERNEL_CMDLINE_EXTRA:-}" ]]; then
+ kernel_cmdline+=" ${KERNEL_CMDLINE_EXTRA}"
+ fi
+
${QEMU_BINARY} \
-nodefaults \
-display none \
@@ -294,7 +308,7 @@ EOF
-m 4G \
-drive file="${rootfs_img}",format=raw,index=1,media=disk,if=virtio,cache=none \
-kernel "${kernel_bzimage}" \
- -append "root=/dev/vda rw console=${QEMU_CONSOLE}"
+ -append "${kernel_cmdline}"
}
copy_logs()
|
| 49/3 | 2026/08/28 18:33 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 25810 output: 20 thoughts: 782 Thoughts: **KMSAN Fuzzing Evaluation for This Patch Series** Okay, let's break down this patch series and see if we need to spin up a dedicated KMSAN fuzzing session. My first step is always understanding what KMSAN is looking for: uninitialized memory reads – stack, heap, anywhere really. Also, any potential kernel-to-user memory leaks where uninitialized data is copied out. Finally, it flags branching based on uninitialized variables. Now, let's look at the actual code changes. I see these files have been touched: `Documentation/admin-guide/kernel-parameters.txt`, `Documentation/bpf/signing.rst`, a few include files like `bpf.h` and `key.h`, a new file `kernel/bpf/keys.c`, and a couple of other files. This looks like BPF-related work, possibly key management. Let's dive into the specifics. `include/linux/bpf.h` introduces `BPF_SIG_KEYRING_BPF`, adds function prototypes for `bpf_lookup_keyring` and `bpf_keyring_enforced`, and also some static inlines returning NULL/false. `include/linux/key.h` now has a declaration for `key_register_bpf_keyring`. `include/uapi/linux/keyctl.h` defines `KEY_SPEC_BPF_KEYRING`. Then we have `kernel/bpf/keys.c`. This is where it gets interesting. I see a `bpf_keyring` variable, a `bpf_keyring_unsealed` module parameter, and `bpf_keyring_enforced`. The function `bpf_lookup_keyring` seems to be returning a `struct bpf_key`...but then I'm stopped short, as I look to see `kmalloc_obj`. I'm going to have to look at the definition of `struct bpf_key`, and also other functions like `bpf_lookup_user_key` or `bpf_lookup_system_key` and how they use `struct bpf_key`. Time to grep for `struct bpf_key`. |
| 50/3 | 2026/08/28 18:33 | tool | grepper |
0mArgs:{
"Expression": "struct bpf_key \\{"
}Results: {
"Output": "include/linux/bpf.h=3804=static inline int bpf_fd_reuseport_array_update_elem(struct bpf_map *map,\n--\ninclude/linux/bpf.h-3813-#ifdef CONFIG_KEYS\ninclude/linux/bpf.h:3814:struct bpf_key {\ninclude/linux/bpf.h-3815-\tstruct key *key;\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-25-\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:26:struct bpf_key {} __attribute__((preserve_access_index));\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-27-\n"
} |
| 51/3 | 2026/08/28 18:33 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 27299 output: 37 thoughts: 62 Thoughts: Here's my take on those thoughts: **Deep Dive into BPF Key Lookup Mechanisms** Okay, so I need to understand how these BPF key lookups actually work. My initial thought is to dive into the kernel source, specifically `include/linux/bpf.h` around line 3814. That's usually where the core BPF definitions and structures are located, so I expect to find some key information on the data structures involved in key management there. Then, I'll need to figure out where the actual lookup functions themselves are defined. I'm pretty sure `bpf_lookup_system_key` and `bpf_lookup_user_key` are the functions of interest. I'm going to look in `kernel/bpf/bpf_lsm.c`, or anywhere else that makes sense for kernel code. I'm hoping to understand the logic flow, what data structures are accessed, and any checks or security considerations involved in retrieving these keys. I'm expecting some kind of hash table or radix tree lookup, and I need to see how they deal with the differences between system and user keys. This should shed light on the differences between user-space and kernel-space access. |
| 52/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "include/linux/bpf.h",
"FirstLine": 3810,
"LineCount": 35
}Results: {
"Contents": "3810:\t#endif /* CONFIG_BPF_SYSCALL */\n3811:\t#endif /* defined(CONFIG_INET) \u0026\u0026 defined(CONFIG_BPF_SYSCALL) */\n3812:\t\n3813:\t#ifdef CONFIG_KEYS\n3814:\tstruct bpf_key {\n3815:\t\tstruct key *key;\n3816:\t\tbool has_ref;\n3817:\t};\n3818:\t#endif /* CONFIG_KEYS */\n3819:\t\n3820:\t#if defined(CONFIG_KEYS) \u0026\u0026 defined(CONFIG_BPF_SYSCALL)\n3821:\tstruct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);\n3822:\tstruct bpf_key *bpf_lookup_system_key(u64 id);\n3823:\tstruct bpf_key *bpf_lookup_keyring(void);\n3824:\tbool bpf_keyring_enforced(void);\n3825:\tvoid bpf_key_put(struct bpf_key *bkey);\n3826:\tint bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\n3827:\t\t\t\t const struct bpf_dynptr *sig_p,\n3828:\t\t\t\t struct bpf_key *trusted_keyring);\n3829:\t\n3830:\tstatic inline s32 bpf_key_serial(const struct bpf_key *key)\n3831:\t{\n3832:\t\treturn key-\u003ehas_ref ? key-\u003ekey-\u003eserial : 0;\n3833:\t}\n3834:\t#else\n3835:\tstatic inline struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)\n3836:\t{\n3837:\t\treturn NULL;\n3838:\t}\n3839:\t\n3840:\tstatic inline struct bpf_key *bpf_lookup_system_key(u64 id)\n3841:\t{\n3842:\t\treturn NULL;\n3843:\t}\n3844:\t\n"
} |
| 53/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 28238 output: 22 thoughts: 36 Thoughts: Okay, here's my interpretation of those thoughts, summarized in the first person, assuming the context of someone experienced in eBPF: **Digging into the Key's Role** Alright, time to dive in and understand how this all fits together. My initial question is: where exactly is this `bpf_lookup_system_key` function defined? I need to see its implementation, its arguments, and what it's ultimately doing to get a handle on how it operates. That's the foundation. Then, I need to focus on `bpf_prog_verify_signature` and how it's leveraging `bpf_key`. I expect the key to play a critical role in the signature verification process. Is it used to look up a corresponding signature, a public key, or some other security-relevant information? Understanding this connection is paramount. I'll need to trace how `bpf_key` is passed around, and see where and how its properties are used during the signature verification. I am assuming the key is a pointer or index, I need to see what data the system is using. |
| 54/3 | 2026/08/28 18:34 | tool | grepper |
0mArgs:{
"Expression": "bpf_lookup_system_key"
}Results: {
"Output": "include/linux/bpf.h=3821=struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);\ninclude/linux/bpf.h:3822:struct bpf_key *bpf_lookup_system_key(u64 id);\ninclude/linux/bpf.h-3823-struct bpf_key *bpf_lookup_keyring(void);\n--\ninclude/linux/bpf.h=3835=static inline struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)\n--\ninclude/linux/bpf.h-3839-\ninclude/linux/bpf.h:3840:static inline struct bpf_key *bpf_lookup_system_key(u64 id)\ninclude/linux/bpf.h-3841-{\n--\nkernel/bpf/helpers.c=4226=__bpf_kfunc struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags)\n--\nkernel/bpf/helpers.c-4254-/**\nkernel/bpf/helpers.c:4255: * bpf_lookup_system_key - lookup a key by a system-defined ID\nkernel/bpf/helpers.c-4256- * @id: key ID\n--\nkernel/bpf/helpers.c-4274- */\nkernel/bpf/helpers.c:4275:__bpf_kfunc struct bpf_key *bpf_lookup_system_key(u64 id)\nkernel/bpf/helpers.c-4276-{\n--\nkernel/bpf/helpers.c=4859=BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)\nkernel/bpf/helpers.c:4860:BTF_ID_FLAGS(func, bpf_lookup_system_key, KF_ACQUIRE | KF_RET_NULL)\nkernel/bpf/helpers.c-4861-BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21027-\tif (system_keyring_id_check(attr-\u003ekeyring_id) == 0) {\nkernel/bpf/verifier.c:21028:\t\tkey = bpf_lookup_system_key(attr-\u003ekeyring_id);\nkernel/bpf/verifier.c-21029-\t} else if (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n--\ntools/testing/selftests/bpf/bpf_kfuncs.h=75=extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym;\ntools/testing/selftests/bpf/bpf_kfuncs.h:76:extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;\ntools/testing/selftests/bpf/bpf_kfuncs.h-77-extern void bpf_key_put(struct bpf_key *key) __ksym;\n--\ntools/testing/selftests/bpf/prog_tests/lookup_key.c=19=static int libbpf_print_cb(enum libbpf_print_level level, const char *fmt,\n--\ntools/testing/selftests/bpf/prog_tests/lookup_key.c-29-\tif (strcmp(func, \"bpf_lookup_user_key\") \u0026\u0026 strcmp(func, \"bpf_key_put\") \u0026\u0026\ntools/testing/selftests/bpf/prog_tests/lookup_key.c:30:\t strcmp(func, \"bpf_lookup_system_key\"))\ntools/testing/selftests/bpf/prog_tests/lookup_key.c-31-\t\treturn 0;\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c=52=int BPF_PROG(dynptr_data_null, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-70-\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c:71:\ttrusted_keyring = bpf_lookup_system_key(0);\ntools/testing/selftests/bpf/progs/test_kfunc_dynptr_param.c-72-\tif (!trusted_keyring)\n--\ntools/testing/selftests/bpf/progs/test_lookup_key.c=21=extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym;\ntools/testing/selftests/bpf/progs/test_lookup_key.c:22:extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;\ntools/testing/selftests/bpf/progs/test_lookup_key.c-23-extern void bpf_key_put(struct bpf_key *key) __ksym;\n--\ntools/testing/selftests/bpf/progs/test_lookup_key.c=26=int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_lookup_key.c-37-\telse\ntools/testing/selftests/bpf/progs/test_lookup_key.c:38:\t\tbkey = bpf_lookup_system_key(key_id);\ntools/testing/selftests/bpf/progs/test_lookup_key.c-39-\n--\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c=40=int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)\n--\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c-78-\telse\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c:79:\t\ttrusted_keyring = bpf_lookup_system_key(system_keyring_id);\ntools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c-80-\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=28=extern void bpf_key_put(struct bpf_key *key) __ksym;\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:29:extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-30-extern struct bpf_key *bpf_lookup_user_key(__s32 serial, __u64 flags) __ksym;\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=37=void __kfunc_btf_root(void)\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-39-\tbpf_key_put(0);\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:40:\tbpf_lookup_system_key(0);\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-41-\tbpf_lookup_user_key(0, 0);\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=248=__naked void acquire_release_system_key_reference(void)\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-251-\tr1 = 1;\t\t\t\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:252:\tcall %[bpf_lookup_system_key];\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-253-\tif r0 == 0 goto l0_%=;\t\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=256=l0_%=:\tr0 = 0;\t\t\t\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-259-\t: __imm(bpf_key_put),\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:260:\t __imm(bpf_lookup_system_key)\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-261-\t: __clobber_all);\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=286=__naked void system_key_reference_without_check(void)\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-289-\tr1 = 1;\t\t\t\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:290:\tcall %[bpf_lookup_system_key];\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-291-\tr1 = r0;\t\t\t\t\t\\\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-296-\t: __imm(bpf_key_put),\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:297:\t __imm(bpf_lookup_system_key)\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-298-\t: __clobber_all);\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c=334=__naked void potential_reference_to_system_key(void)\n--\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-337-\tr1 = 1;\t\t\t\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:338:\tcall %[bpf_lookup_system_key];\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-339-\texit;\t\t\t\t\t\t\\\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-340-\"\t:\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c:341:\t: __imm(bpf_lookup_system_key)\ntools/testing/selftests/bpf/progs/verifier_ref_tracking.c-342-\t: __clobber_all);\n"
} |
| 55/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 31421 output: 37 thoughts: 32 Thoughts: **Examining `bpf_lookup_system_key` Initialization** Okay, so I need to understand how `bpf_lookup_system_key` handles the initialization of `bkey`. I'll jump into `kernel/bpf/helpers.c` and take a look at the relevant code. Specifically, I'm interested in how that `bkey` structure is set up. This function is critical for how BPF interacts with system keys, and understanding the initialization process is key to grasping its overall behavior and potential performance characteristics, and any likely gotchas in how it handles memory and potential race conditions. Let's see how they're handling the key data and any related locking mechanisms, if any. The initialization of that structure is a critical jumping off point for understanding the overall system key lookup process. |
| 56/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/helpers.c",
"FirstLine": 4255,
"LineCount": 40
}Results: {
"Contents": "4255:\t * bpf_lookup_system_key - lookup a key by a system-defined ID\n4256:\t * @id: key ID\n4257:\t *\n4258:\t * Obtain a bpf_key structure with a key pointer set to the passed key ID.\n4259:\t * The key pointer is marked as invalid, to prevent bpf_key_put() from\n4260:\t * attempting to decrement the key reference count on that pointer. The key\n4261:\t * pointer set in such way is currently understood only by\n4262:\t * verify_pkcs7_signature().\n4263:\t *\n4264:\t * Set *id* to one of the values defined in include/linux/verification.h:\n4265:\t * 0 for the primary keyring (immutable keyring of system keys);\n4266:\t * VERIFY_USE_SECONDARY_KEYRING for both the primary and secondary keyring\n4267:\t * (where keys can be added only if they are vouched for by existing keys\n4268:\t * in those keyrings); VERIFY_USE_PLATFORM_KEYRING for the platform\n4269:\t * keyring (primarily used by the integrity subsystem to verify a kexec'ed\n4270:\t * kerned image and, possibly, the initramfs signature).\n4271:\t *\n4272:\t * Return: a bpf_key pointer with an invalid key pointer set from the\n4273:\t * pre-determined ID on success, a NULL pointer otherwise\n4274:\t */\n4275:\t__bpf_kfunc struct bpf_key *bpf_lookup_system_key(u64 id)\n4276:\t{\n4277:\t\tstruct bpf_key *bkey;\n4278:\t\n4279:\t\tif (system_keyring_id_check(id) \u003c 0)\n4280:\t\t\treturn NULL;\n4281:\t\n4282:\t\tbkey = kmalloc_obj(*bkey, GFP_ATOMIC);\n4283:\t\tif (!bkey)\n4284:\t\t\treturn NULL;\n4285:\t\n4286:\t\tbkey-\u003ekey = (struct key *)(unsigned long)id;\n4287:\t\tbkey-\u003ehas_ref = false;\n4288:\t\n4289:\t\treturn bkey;\n4290:\t}\n4291:\t\n4292:\t/**\n4293:\t * bpf_key_put - decrement key reference count if key is valid and free bpf_key\n4294:\t * @bkey: bpf_key structure\n"
} |
| 57/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 32343 output: 38 thoughts: 481 Thoughts: **Analyzing the BPF Keyring Lookup** Okay, let's dive into this `bpf_lookup_keyring` function in `kernel/bpf/keys.c`. The first thing I see is the function's purpose: it retrieves a `bpf_key` structure. I immediately notice the null check: it returns `NULL` if `bpf_keyring` is null or if the keyring seems empty (no leaves or restrictions). Interesting. That's a reasonable safeguard. Now, let me look at how `bpf_key` is defined: a `key` pointer and a boolean `has_ref`. The function allocates memory for a `bpf_key` structure and then explicitly sets both `key` to `bpf_keyring` and `has_ref` to false. This is pretty straightforward. `has_ref` being false suggests this lookup is providing a *reference* and that there's not an immediate requirement to release the key. Now the million-dollar question: where is this function actually *used*? I see it's called in `bpf_prog_verify_signature`, which is in the verifier. That's good context. Let's look at the function in more detail. `bpf_prog_verify_signature` does a few checks. If `system_keyring_id_check` passes, it uses `bpf_lookup_system_key`. Otherwise, if the ID is `KEY_SPEC_BPF_KEYRING`, it's now calling my function, `bpf_lookup_keyring`. If there's an enforcement policy and the supplied keyring is invalid, it returns `EPERM`. Otherwise it uses `bpf_lookup_user_key`. I'll need to go to the verifier to see how this all fits into the bigger picture. Specifically, lines 21000-21080 of `kernel/bpf/verifier.c` looks like the spot. I need to understand how the signature verification process interacts with the BPF keyring. |
| 58/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 21000,
"LineCount": 80
}Results: {
"Contents": "21000:\t/*\n21001:\t * Verify the PKCS#7 signature of a loaded program. Called from bpf_check()\n21002:\t * once the program's metadata maps have been resolved into used_maps, so\n21003:\t * the exact maps folded into the signature are the ones the program binds.\n21004:\t *\n21005:\t * The signature covers the instructions followed by the frozen contents of\n21006:\t * each map, in @maps order: insns || map_0 || map_1 || [...]. On success the\n21007:\t * verdict and keyring info are recorded on prog-\u003eaux.\n21008:\t */\n21009:\tstatic int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n21010:\t\t\t\t\t union bpf_attr *attr, bool is_kernel)\n21011:\t{\n21012:\t\tbpfptr_t usig = make_bpfptr(attr-\u003esignature, is_kernel);\n21013:\t\tstruct bpf_dynptr_kern sig_ptr, data_ptr;\n21014:\t\tstruct bpf_prog *prog = env-\u003eprog;\n21015:\t\tstruct bpf_map **maps = env-\u003eused_maps;\n21016:\t\tstruct bpf_key *key = NULL;\n21017:\t\tvoid *sig, *data = NULL;\n21018:\t\tu32 map_cnt = env-\u003eused_map_cnt;\n21019:\t\tu32 i, off, insns_sz;\n21020:\t\tu64 data_sz;\n21021:\t\tint err = 0;\n21022:\t\n21023:\t\tif (!attr-\u003esignature_size ||\n21024:\t\t attr-\u003esignature_size \u003e BPF_PROG_MAX_SIGNATURE_SIZE)\n21025:\t\t\treturn -EINVAL;\n21026:\t\n21027:\t\tif (system_keyring_id_check(attr-\u003ekeyring_id) == 0) {\n21028:\t\t\tkey = bpf_lookup_system_key(attr-\u003ekeyring_id);\n21029:\t\t} else if (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n21030:\t\t\tkey = bpf_lookup_keyring();\n21031:\t\t} else if (bpf_keyring_enforced()) {\n21032:\t\t\tverbose(env, \"caller-supplied keyring refused, use bpf keyring\\n\");\n21033:\t\t\treturn -EPERM;\n21034:\t\t} else {\n21035:\t\t\tkey = bpf_lookup_user_key(attr-\u003ekeyring_id, 0);\n21036:\t\t}\n21037:\t\tif (!key) {\n21038:\t\t\tif (attr-\u003ekeyring_id == KEY_SPEC_BPF_KEYRING) {\n21039:\t\t\t\tverbose(env, \"the bpf keyring is empty or has not been restricted\\n\");\n21040:\t\t\t\treturn -ENOKEY;\n21041:\t\t\t}\n21042:\t\t\tverbose(env, \"cannot resolve signing keyring with keyring_id %d\\n\",\n21043:\t\t\t\tattr-\u003ekeyring_id);\n21044:\t\t\treturn -EINVAL;\n21045:\t\t}\n21046:\t\n21047:\t\tsig = kvmemdup_bpfptr(usig, attr-\u003esignature_size);\n21048:\t\tif (IS_ERR(sig)) {\n21049:\t\t\tbpf_key_put(key);\n21050:\t\t\treturn PTR_ERR(sig);\n21051:\t\t}\n21052:\t\n21053:\t\tinsns_sz = prog-\u003elen * sizeof(struct bpf_insn);\n21054:\t\tdata_sz = insns_sz;\n21055:\t\tfor (i = 0; i \u003c map_cnt; i++) {\n21056:\t\t\tstruct bpf_map *map = maps[i];\n21057:\t\n21058:\t\t\tif (map-\u003emap_type != BPF_MAP_TYPE_ARRAY ||\n21059:\t\t\t !map-\u003eops-\u003emap_direct_value_addr) {\n21060:\t\t\t\tverbose(env, \"signed program metadata map '%s' must be an array\\n\",\n21061:\t\t\t\t\tmap-\u003ename);\n21062:\t\t\t\terr = -EINVAL;\n21063:\t\t\t\tgoto out;\n21064:\t\t\t}\n21065:\t\t\tif (!READ_ONCE(map-\u003efrozen)) {\n21066:\t\t\t\tverbose(env, \"signed program metadata map '%s' must be frozen\\n\",\n21067:\t\t\t\t\tmap-\u003ename);\n21068:\t\t\t\terr = -EPERM;\n21069:\t\t\t\tgoto out;\n21070:\t\t\t}\n21071:\t\t\tif (bpf_map_write_active(map)) {\n21072:\t\t\t\tverbose(env, \"signed program metadata map '%s' has active writers\\n\",\n21073:\t\t\t\t\tmap-\u003ename);\n21074:\t\t\t\terr = -EBUSY;\n21075:\t\t\t\tgoto out;\n21076:\t\t\t}\n21077:\t\t\tif (!map-\u003eexcl_prog_sha) {\n21078:\t\t\t\tverbose(env, \"signed program metadata map '%s' must be exclusive\\n\",\n21079:\t\t\t\t\tmap-\u003ename);\n"
} |
| 59/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 34764 output: 37 thoughts: 136 Thoughts: **Analyzing Kernel Key Management** Okay, let's dive into the kernel's key management, specifically focusing on `security/keys/process_keys.c`. I need to understand how the `key_register_bpf_keyring` and `lookup_user_key` functions interact. My initial observation is this snippet: ```c case KEY_SPEC_BPF_KEYRING: key = bpf_keyring; if (!key) goto error; __key_get(key); key_ref = make_key_ref(key, 0); break; ``` It seems like when dealing with `KEY_SPEC_BPF_KEYRING`, the code directly uses the `bpf_keyring`. It's fetching the key if it exists, increasing its reference count and making a key reference. I need to understand the implications of this for BPF programs and how they interact with keys. I'm now going to focus my attention on lines 740-770 in `security/keys/process_keys.c`. I have a suspicion that this section holds critical information on how these key operations are handled. I expect to find some of the critical parts here. |
| 60/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "security/keys/process_keys.c",
"FirstLine": 740,
"LineCount": 40
}Results: {
"Contents": " 740:\t\n 741:\t\tcase KEY_SPEC_REQUESTOR_KEYRING:\n 742:\t\t\tif (!ctx.cred-\u003erequest_key_auth)\n 743:\t\t\t\tgoto error;\n 744:\t\n 745:\t\t\tdown_read(\u0026ctx.cred-\u003erequest_key_auth-\u003esem);\n 746:\t\t\tif (test_bit(KEY_FLAG_REVOKED,\n 747:\t\t\t\t \u0026ctx.cred-\u003erequest_key_auth-\u003eflags)) {\n 748:\t\t\t\tkey_ref = ERR_PTR(-EKEYREVOKED);\n 749:\t\t\t\tkey = NULL;\n 750:\t\t\t} else {\n 751:\t\t\t\trka = ctx.cred-\u003erequest_key_auth-\u003epayload.data[0];\n 752:\t\t\t\tkey = rka-\u003edest_keyring;\n 753:\t\t\t\t__key_get(key);\n 754:\t\t\t}\n 755:\t\t\tup_read(\u0026ctx.cred-\u003erequest_key_auth-\u003esem);\n 756:\t\t\tif (!key)\n 757:\t\t\t\tgoto error;\n 758:\t\t\tkey_ref = make_key_ref(key, 1);\n 759:\t\t\tbreak;\n 760:\t\n 761:\t\tcase KEY_SPEC_BPF_KEYRING:\n 762:\t\t\tkey = bpf_keyring;\n 763:\t\t\tif (!key)\n 764:\t\t\t\tgoto error;\n 765:\t\t\t__key_get(key);\n 766:\t\t\tkey_ref = make_key_ref(key, 0);\n 767:\t\t\tbreak;\n 768:\t\n 769:\t\tdefault:\n 770:\t\t\tkey_ref = ERR_PTR(-EINVAL);\n 771:\t\t\tif (id \u003c 1)\n 772:\t\t\t\tgoto error;\n 773:\t\n 774:\t\t\tkey = key_lookup(id);\n 775:\t\t\tif (IS_ERR(key)) {\n 776:\t\t\t\tkey_ref = ERR_CAST(key);\n 777:\t\t\t\tgoto error;\n 778:\t\t\t}\n 779:\t\n"
} |
| 61/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 35774 output: 37 thoughts: 24 Thoughts: Okay, here's my attempt at summarizing those thoughts from a first-person, expert perspective, aiming for a fulsome rendering: **Investigating `KEY_SPEC_*` Keyring Handling in `lookup_user_key`** Alright, let's dive in. I need to take a closer look at how the `lookup_user_key` function interacts with those `KEY_SPEC_*` keyrings. My gut tells me there might be some nuances or potential edge cases I need to consider, especially given the various special key types those constants represent. I'm thinking specifically about how the kernel interprets and handles the requests when these specific keyring types are involved in a lookup. Are there any deviations from the standard keyring behavior I need to be aware of? Are there specific checks or optimizations applied? I want to make sure I've got a solid understanding of the complete lifecycle of how these special keyrings are used when we're trying to retrieve a key. I should also check to see if any security considerations are implicitly being handled here. This investigation is key to understanding the overall structure of key management. I know it's a critical area, so I'll be thorough. |
| 62/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "security/keys/process_keys.c",
"FirstLine": 690,
"LineCount": 55
}Results: {
"Contents": " 690:\t\t\t\tif (lflags \u0026 KEY_LOOKUP_CREATE)\n 691:\t\t\t\t\tret = join_session_keyring(NULL);\n 692:\t\t\t\telse\n 693:\t\t\t\t\tret = install_session_keyring(user_session);\n 694:\t\n 695:\t\t\t\tkey_put(user_session);\n 696:\t\t\t\tif (ret \u003c 0)\n 697:\t\t\t\t\tgoto error;\n 698:\t\t\t\tgoto reget_creds;\n 699:\t\t\t} else if (test_bit(KEY_FLAG_UID_KEYRING,\n 700:\t\t\t\t\t \u0026ctx.cred-\u003esession_keyring-\u003eflags) \u0026\u0026\n 701:\t\t\t\t lflags \u0026 KEY_LOOKUP_CREATE) {\n 702:\t\t\t\tret = join_session_keyring(NULL);\n 703:\t\t\t\tif (ret \u003c 0)\n 704:\t\t\t\t\tgoto error;\n 705:\t\t\t\tgoto reget_creds;\n 706:\t\t\t}\n 707:\t\n 708:\t\t\tkey = ctx.cred-\u003esession_keyring;\n 709:\t\t\t__key_get(key);\n 710:\t\t\tkey_ref = make_key_ref(key, 1);\n 711:\t\t\tbreak;\n 712:\t\n 713:\t\tcase KEY_SPEC_USER_KEYRING:\n 714:\t\t\tret = look_up_user_keyrings(\u0026key, NULL);\n 715:\t\t\tif (ret \u003c 0)\n 716:\t\t\t\tgoto error;\n 717:\t\t\tkey_ref = make_key_ref(key, 1);\n 718:\t\t\tbreak;\n 719:\t\n 720:\t\tcase KEY_SPEC_USER_SESSION_KEYRING:\n 721:\t\t\tret = look_up_user_keyrings(NULL, \u0026key);\n 722:\t\t\tif (ret \u003c 0)\n 723:\t\t\t\tgoto error;\n 724:\t\t\tkey_ref = make_key_ref(key, 1);\n 725:\t\t\tbreak;\n 726:\t\n 727:\t\tcase KEY_SPEC_GROUP_KEYRING:\n 728:\t\t\t/* group keyrings are not yet supported */\n 729:\t\t\tkey_ref = ERR_PTR(-EINVAL);\n 730:\t\t\tgoto error;\n 731:\t\n 732:\t\tcase KEY_SPEC_REQKEY_AUTH_KEY:\n 733:\t\t\tkey = ctx.cred-\u003erequest_key_auth;\n 734:\t\t\tif (!key)\n 735:\t\t\t\tgoto error;\n 736:\t\n 737:\t\t\t__key_get(key);\n 738:\t\t\tkey_ref = make_key_ref(key, 1);\n 739:\t\t\tbreak;\n 740:\t\n 741:\t\tcase KEY_SPEC_REQUESTOR_KEYRING:\n 742:\t\t\tif (!ctx.cred-\u003erequest_key_auth)\n 743:\t\t\t\tgoto error;\n 744:\t\n"
} |
| 63/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 36869 output: 29 thoughts: 37 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Signature Verification in the BPF Verifier** Alright, so the signature verification in the BPF verifier is passing. That's good, but *why*? I need to understand exactly how the `prog->aux->sig` field, or the keyring type/serial information, is being set within `verifier.c` once `bpf_prog_verify_signature` returns success. I'm assuming that the verification is working correctly at a lower level but I need to fully understand the flow of the verifier to make sure. I've got to trace the path and data flow after the signature check succeeds to see if those key pieces of information are being populated as expected. I need to make sure the verification has the information it needs, and is doing what I expect it to do. This is critical for downstream operations and proper security context propagation, and I want to double check the specifics. |
| 64/3 | 2026/08/28 18:34 | tool | grepper |
0mArgs:{
"Expression": "keyring_type",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/syscall.c=2917=static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_attr *attr_log)\n--\nkernel/bpf/syscall.c-3084-\tprog-\u003egpl_compatible = license_is_gpl_compatible(license) ? 1 : 0;\nkernel/bpf/syscall.c:3085:\tprog-\u003eaux-\u003esig.keyring_type = BPF_SIG_KEYRING_NONE;\nkernel/bpf/syscall.c-3086-\tprog-\u003eaux-\u003esig.verdict = BPF_SIG_UNSIGNED;\n--\nkernel/bpf/verifier.c=21009=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-21122-\t\tprog-\u003eaux-\u003esig.keyring_serial = bpf_key_serial(key);\nkernel/bpf/verifier.c:21123:\t\tprog-\u003eaux-\u003esig.keyring_type = bpf_classify_keyring(attr-\u003ekeyring_id);\nkernel/bpf/verifier.c-21124-\t\tprog-\u003eaux-\u003esig.verdict = BPF_SIG_VERIFIED;\n"
} |
| 65/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 37477 output: 38 thoughts: 23 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Investigating `bpf_prog_verify_signature`** Okay, so I need to dig into this. Based on the current issue, my instinct tells me that the problem might be originating somewhere in the `bpf_prog_verify_signature` function. I think it's likely to be around line 21120. I'll start by taking a look there and see what jumps out at me. I'll be particularly interested in anything related to access control, signature validation logic, and error handling in that area. Hopefully, that will give me a better understanding of what's happening. From there, I can drill down further and either isolate the root cause or at least narrow the possibilities. |
| 66/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 21110,
"LineCount": 30
}Results: {
"Contents": "21110:\t\t}\n21111:\t\n21112:\t\tbpf_dynptr_init(\u0026data_ptr, data, BPF_DYNPTR_TYPE_LOCAL, 0, data_sz);\n21113:\t\tbpf_dynptr_init(\u0026sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0,\n21114:\t\t\t\tattr-\u003esignature_size);\n21115:\t\n21116:\t\terr = bpf_verify_pkcs7_signature((struct bpf_dynptr *)\u0026data_ptr,\n21117:\t\t\t\t\t\t (struct bpf_dynptr *)\u0026sig_ptr, key);\n21118:\t\tif (err) {\n21119:\t\t\tverbose(env, \"signature verification failed: %d\\n\", err);\n21120:\t\t} else {\n21121:\t\t\tverbose(env, \"signature verification passed\\n\");\n21122:\t\t\tprog-\u003eaux-\u003esig.keyring_serial = bpf_key_serial(key);\n21123:\t\t\tprog-\u003eaux-\u003esig.keyring_type = bpf_classify_keyring(attr-\u003ekeyring_id);\n21124:\t\t\tprog-\u003eaux-\u003esig.verdict = BPF_SIG_VERIFIED;\n21125:\t\t}\n21126:\tout:\n21127:\t\tkvfree(data);\n21128:\t\tbpf_key_put(key);\n21129:\t\tkvfree(sig);\n21130:\t\treturn err;\n21131:\t}\n21132:\t\n21133:\tint bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n21134:\t\t struct bpf_log_attr *attr_log)\n21135:\t{\n21136:\t\tu64 start_time = ktime_get_ns();\n21137:\t\tstruct bpf_verifier_env *env;\n21138:\t\tint i, len, ret = -EINVAL, err;\n21139:\t\tbool is_priv;\n"
} |
| 67/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 38281 output: 37 thoughts: 188 Thoughts: **Analyzing BPF Key Serial and Put Operations** Okay, let's dive into this. First, I need to verify how `bpf_key_serial` and `bpf_key_put` interact, especially within the context of the keyring operations. Looking at the code for `bpf_key_serial`, I see it's a simple inline function: it checks the `has_ref` flag on the `bpf_key` structure. If `has_ref` is true, it returns the `serial` field from the underlying key; otherwise, it returns 0. Now, considering how the `bpf_lookup_keyring()` function works, I recall that `key->key` gets assigned `bpf_keyring`, and crucially, `key->has_ref` is set to `false`. Therefore, when `bpf_key_serial(key)` is called subsequently in this scenario, the result will definitively be 0, because `has_ref` is false. The next piece I need to understand is how the keyring itself is classified; `bpf_classify_keyring(attr->keyring_id)` returns `BPF_SIG_KEYRING_BPF`, but this doesn't offer direct insight into the ref-count or serial behavior. Finally, the key function to examine is `bpf_key_put(key)`. I need to consult the implementation of `bpf_key_put` itself, likely within `kernel/bpf/helpers.c`, to understand its implications for releasing the key and managing its lifecycle. This is where the actual ref-counting or deallocation logic will reside. The next logical step is to check how `bpf_key_put` handles a key with `has_ref` set to `false` in this specific use case. |
| 68/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/helpers.c",
"FirstLine": 4290,
"LineCount": 25
}Results: {
"Contents": "4290:\t}\n4291:\t\n4292:\t/**\n4293:\t * bpf_key_put - decrement key reference count if key is valid and free bpf_key\n4294:\t * @bkey: bpf_key structure\n4295:\t *\n4296:\t * Decrement the reference count of the key inside *bkey*, if the pointer\n4297:\t * is valid, and free *bkey*.\n4298:\t */\n4299:\t__bpf_kfunc void bpf_key_put(struct bpf_key *bkey)\n4300:\t{\n4301:\t\tif (bkey-\u003ehas_ref)\n4302:\t\t\tkey_put(bkey-\u003ekey);\n4303:\t\n4304:\t\tkfree(bkey);\n4305:\t}\n4306:\t\n4307:\t/**\n4308:\t * bpf_verify_pkcs7_signature - verify a PKCS#7 signature\n4309:\t * @data_p: data to verify\n4310:\t * @sig_p: signature of the data\n4311:\t * @trusted_keyring: keyring with keys trusted for signature verification\n4312:\t *\n4313:\t * Verify the PKCS#7 signature *sig_ptr* against the supplied *data_ptr*\n4314:\t * with keys in a keyring referenced by *trusted_keyring*.\n"
} |
| 69/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 39284 output: 37 thoughts: 25 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person: **Investigating `bpf_verify_pkcs7_signature` and `bkey` Usage** Alright, let's dive into `bpf_verify_pkcs7_signature`. My immediate focus is understanding how this function interacts with `bkey`. I need to grasp the specific context in which `bkey` is employed within the signature verification process. Are we using it for key material directly? Is it a lookup mechanism? Does this function, and ultimately the BPF program it's part of, leverage `bkey` for cryptographic operations or only for validation metadata? Understanding that relationship is crucial to understanding the function's overall security profile and potential vulnerabilities. I need to trace the data flow through this function to get a good handle. |
| 70/3 | 2026/08/28 18:34 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/helpers.c",
"FirstLine": 4315,
"LineCount": 45
}Results: {
"Contents": "4315:\t *\n4316:\t * Return: 0 on success, a negative value on error.\n4317:\t */\n4318:\t__bpf_kfunc int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\n4319:\t\t\t\t const struct bpf_dynptr *sig_p,\n4320:\t\t\t\t struct bpf_key *trusted_keyring)\n4321:\t{\n4322:\t#ifdef CONFIG_SYSTEM_DATA_VERIFICATION\n4323:\t\tconst struct bpf_dynptr_kern *data_ptr = (struct bpf_dynptr_kern *)data_p;\n4324:\t\tconst struct bpf_dynptr_kern *sig_ptr = (struct bpf_dynptr_kern *)sig_p;\n4325:\t\tconst void *data, *sig;\n4326:\t\tu32 data_len, sig_len;\n4327:\t\tint ret;\n4328:\t\n4329:\t\tif (trusted_keyring-\u003ehas_ref) {\n4330:\t\t\t/*\n4331:\t\t\t * Do the permission check deferred in bpf_lookup_user_key().\n4332:\t\t\t * See bpf_lookup_user_key() for more details.\n4333:\t\t\t *\n4334:\t\t\t * A call to key_task_permission() here would be redundant, as\n4335:\t\t\t * it is already done by keyring_search() called by\n4336:\t\t\t * find_asymmetric_key().\n4337:\t\t\t */\n4338:\t\t\tret = key_validate(trusted_keyring-\u003ekey);\n4339:\t\t\tif (ret \u003c 0)\n4340:\t\t\t\treturn ret;\n4341:\t\t}\n4342:\t\n4343:\t\tdata_len = __bpf_dynptr_size(data_ptr);\n4344:\t\tdata = __bpf_dynptr_data(data_ptr, data_len);\n4345:\t\tif (!data)\n4346:\t\t\treturn -EINVAL;\n4347:\t\n4348:\t\tsig_len = __bpf_dynptr_size(sig_ptr);\n4349:\t\tsig = __bpf_dynptr_data(sig_ptr, sig_len);\n4350:\t\tif (!sig)\n4351:\t\t\treturn -EINVAL;\n4352:\t\n4353:\t\treturn verify_pkcs7_signature(data, data_len, sig, sig_len,\n4354:\t\t\t\t\t trusted_keyring-\u003ekey,\n4355:\t\t\t\t\t VERIFYING_BPF_SIGNATURE, NULL,\n4356:\t\t\t\t\t NULL);\n4357:\t#else\n4358:\t\treturn -EOPNOTSUPP;\n4359:\t#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */\n"
} |
| 71/3 | 2026/08/28 18:34 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 40278 output: 18 thoughts: 1174 Thoughts: **KMSAN/KASAN Analysis of BPF Patch** Alright, let's dive into this patch and systematically assess its potential impact on memory safety, specifically focusing on KMSAN/KASAN concerns. First, I'll scan the patch for any other areas that might require closer examination. Okay, I've got a handle on the changes. My primary focus is on the `bpf_prog_verify_signature` signature size bound change: the check now uses `BPF_PROG_MAX_SIGNATURE_SIZE` (64KB) instead of `KMALLOC_MAX_CACHE_SIZE`. This is a clear improvement – a smaller, more controlled upper bound is always good, and it’s safer in terms of avoiding potential allocation issues. Next up, `bpf_keyring_init`. I need to ensure there are no uninitialized variables or data structures. I see a `keyring` allocation and assignment, along with several conditional checks and calls to `keyring_restrict`, `key_revoke`, `key_put`, and `key_register_bpf_keyring`. The key thing here is the initialization flow, and I don't see anything immediately concerning. `bpf_keyring` is clearly set and should not lead to uninitialized memory being used in subsequent operation. Now, let's step through the criteria for needing KMSAN: 1. **Kernel structures to userspace:** Nope. It doesn't look like any new kernel structures are being copied to userspace. The `BPF_SIG_KEYRING_BPF` enum addition is internal to the kernel and lives in the `prog->aux->sig` state. No problem. 2. **Conditional logic on potentially uninitialized data:** I'll look at the variables. I see `bpf_keyring_unsealed`, `bpf_keyring`, and `bkey`. These look properly initialized. In `bpf_prog_verify_signature`, all key variables are initialized. In `lookup_user_key`, `key` and `key_ref` are properly assigned. It seems this check is clean. 3. **Uninitialized fields in complex data structures:** The `bpf_key` structure is allocated, and the fields are set. No issues there. 4. **Bounds checks, lengths, and access of uninitialized buffer bytes:** The `BPF_PROG_MAX_SIGNATURE_SIZE` check is an improvement, as noted before. `kvmemdup_bpfptr` is copying from the user pointer into a newly allocated kernel buffer. Okay, based on my analysis, this patch appears clean. All the global variables are well-initialized, and there are no info leaks or other bugs related to reading uninitialized memory. Standard KASAN should be sufficient here. I'll just double-check one thing. The `kmalloc_obj(*bkey)` call in `bpf_lookup_keyring`. I wonder if this uses `GFP_KERNEL`? Let me quickly look at the definition of `kmalloc_obj`. I'll run a quick grep to be sure. Alright, I've confirmed. All is well. |
| 72/3 | 2026/08/28 18:35 | tool | grepper |
0mArgs:{
"Expression": "kmalloc_obj"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 15562 lines.\nUse more precise expression if possible.\n\nDocumentation/RCU/Design/Requirements/Requirements.rst=203=non-\\ ``NULL``, locklessly accessing the ``-\u003ea`` and ``-\u003eb`` fields.\n--\nDocumentation/RCU/Design/Requirements/Requirements.rst-208- 2 {\nDocumentation/RCU/Design/Requirements/Requirements.rst:209: 3 p = kmalloc_obj(*p);\nDocumentation/RCU/Design/Requirements/Requirements.rst-210- 4 if (!p)\n--\nDocumentation/RCU/Design/Requirements/Requirements.rst=225=their rights to reorder this code as follows:\n--\nDocumentation/RCU/Design/Requirements/Requirements.rst-230- 2 {\nDocumentation/RCU/Design/Requirements/Requirements.rst:231: 3 p = kmalloc_obj(*p);\nDocumentation/RCU/Design/Requirements/Requirements.rst-232- 4 if (!p)\n--\nDocumentation/RCU/Design/Requirements/Requirements.rst=261=shows an example of insertion:\n--\nDocumentation/RCU/Design/Requirements/Requirements.rst-266- 2 {\nDocumentation/RCU/Design/Requirements/Requirements.rst:267: 3 p = kmalloc_obj(*p);\nDocumentation/RCU/Design/Requirements/Requirements.rst-268- 4 if (!p)\n--\nDocumentation/RCU/listRCU.rst=267=The RCU version of audit_upd_rule() is as follows::\n--\nDocumentation/RCU/listRCU.rst-278-\t\t\tif (!audit_compare_rule(rule, \u0026e-\u003erule)) {\nDocumentation/RCU/listRCU.rst:279:\t\t\t\tne = kmalloc_obj(*entry, GFP_ATOMIC);\nDocumentation/RCU/listRCU.rst-280-\t\t\t\tif (ne == NULL)\n--\nDocumentation/RCU/whatisRCU.rst=441=uses of RCU may be found in listRCU.rst and NMI-RCU.rst.\n--\nDocumentation/RCU/whatisRCU.rst-470-\nDocumentation/RCU/whatisRCU.rst:471:\t\tnew_fp = kmalloc_obj(*new_fp);\nDocumentation/RCU/whatisRCU.rst-472-\t\tspin_lock(\u0026foo_mutex);\n--\nDocumentation/RCU/whatisRCU.rst=553=The foo_update_a() function might then be written as follows::\n--\nDocumentation/RCU/whatisRCU.rst-572-\nDocumentation/RCU/whatisRCU.rst:573:\t\tnew_fp = kmalloc_obj(*new_fp);\nDocumentation/RCU/whatisRCU.rst-574-\t\tspin_lock(\u0026foo_mutex);\n--\nDocumentation/core-api/kref.rst=39=kref_init as so::\n--\nDocumentation/core-api/kref.rst-42-\nDocumentation/core-api/kref.rst:43: data = kmalloc_obj(*data);\nDocumentation/core-api/kref.rst-44- if (!data)\n--\nDocumentation/core-api/kref.rst=81=thread to process::\n--\nDocumentation/core-api/kref.rst-102-\tstruct task_struct *task;\nDocumentation/core-api/kref.rst:103:\tdata = kmalloc_obj(*data);\nDocumentation/core-api/kref.rst-104-\tif (!data)\n--\nDocumentation/kernel-hacking/locking.rst=383=to protect the cache and all the objects within it. Here's the code::\n--\nDocumentation/kernel-hacking/locking.rst-444-\nDocumentation/kernel-hacking/locking.rst:445: if ((obj = kmalloc_obj(*obj)) == NULL)\nDocumentation/kernel-hacking/locking.rst-446- return -ENOMEM;\n--\nDocumentation/kernel-hacking/locking.rst=499=which are taken away, and the ``+`` are lines which are added.\n--\nDocumentation/kernel-hacking/locking.rst-519-\nDocumentation/kernel-hacking/locking.rst:520: if ((obj = kmalloc_obj(*obj)) == NULL)\nDocumentation/kernel-hacking/locking.rst-521- return -ENOMEM;\n--\nDocumentation/locking/locktypes.rst=498=works perfectly::\n--\nDocumentation/locking/locktypes.rst-500- raw_spin_lock(\u0026lock);\nDocumentation/locking/locktypes.rst:501: p = kmalloc_obj(*p, GFP_ATOMIC);\nDocumentation/locking/locktypes.rst-502-\n--\nDocumentation/locking/locktypes.rst=507=preemption on PREEMPT_RT kernels::\n--\nDocumentation/locking/locktypes.rst-509- spin_lock(\u0026lock);\nDocumentation/locking/locktypes.rst:510: p = kmalloc_obj(*p, GFP_ATOMIC);\nDocumentation/locking/locktypes.rst-511-\n--\nDocumentation/process/coding-style.rst=938=The kernel provides the following general purpose memory allocators:\nDocumentation/process/coding-style.rst:939:kmalloc(), kzalloc(), kmalloc_objs(), kzalloc_objs(), vmalloc(), and\nDocumentation/process/coding-style.rst-940-vzalloc(). Please refer to the API documentation for further information\n--\nDocumentation/process/coding-style.rst=944=The preferred form for passing a size of a struct is the following:\n--\nDocumentation/process/coding-style.rst-947-\nDocumentation/process/coding-style.rst:948:\tp = kmalloc_obj(*p, ...);\nDocumentation/process/coding-style.rst-949-\n--\nDocumentation/process/coding-style.rst=958=The preferred form for allocating an array is the following:\n--\nDocumentation/process/coding-style.rst-961-\nDocumentation/process/coding-style.rst:962:\tp = kmalloc_objs(*p, n, ...);\nDocumentation/process/coding-style.rst-963-\n--\nDocumentation/process/deprecated.rst=386=may help with alignment, wrap-around, or additional hardening. The\nDocumentation/process/deprecated.rst:387:kmalloc_obj()-family of macros provide this introspection, which can be\nDocumentation/process/deprecated.rst-388-used for the common code patterns for single, array, and flexible object\n--\nDocumentation/process/deprecated.rst=398=become, respectively::\nDocumentation/process/deprecated.rst-399-\nDocumentation/process/deprecated.rst:400:\tptr = kmalloc_obj(*ptr [, gfp] );\nDocumentation/process/deprecated.rst-401-\tptr = kzalloc_obj(*ptr [, gfp] );\nDocumentation/process/deprecated.rst:402:\tptr = kmalloc_objs(*ptr, count [, gfp] );\nDocumentation/process/deprecated.rst-403-\tptr = kzalloc_objs(*ptr, count [, gfp] );\nDocumentation/process/deprecated.rst-404-\tptr = kmalloc_flex(*ptr, flex_member, count [, gfp] );\nDocumentation/process/deprecated.rst:405:\t__auto_type ptr = kmalloc_obj(struct foo [, gfp] );\nDocumentation/process/deprecated.rst-406-\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=1734=callback::\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-1739- ....\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:1740: data = kmalloc_obj(*data);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-1741- substream-\u003eruntime-\u003eprivate_data = data;\n--\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst=3302=destructor function is set in the ``private_free`` field::\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-3303-\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst:3304: struct mydata *p = kmalloc_obj(*p);\nDocumentation/sound/kernel-api/writing-an-alsa-driver.rst-3305- hw-\u003eprivate_data = p;\n--\nDocumentation/spi/spi-summary.rst=242=And SOC-specific utility code might look something like::\n--\nDocumentation/spi/spi-summary.rst-251-\nDocumentation/spi/spi-summary.rst:252:\t\tpdata2 = kmalloc_obj(*pdata2);\nDocumentation/spi/spi-summary.rst-253-\t\t*pdata2 = pdata;\n--\nDocumentation/translations/it_IT/kernel-hacking/locking.rst=403=e tutti gli oggetti che contiene. Ecco il codice::\n--\nDocumentation/translations/it_IT/kernel-hacking/locking.rst-464-\nDocumentation/translations/it_IT/kernel-hacking/locking.rst:465: if ((obj = kmalloc_obj(*obj)) == NULL)\nDocumentation/translations/it_IT/kernel-hacking/locking.rst-466- return -ENOMEM;\n--\nDocumentation/translations/it_IT/kernel-hacking/locking.rst=519=sono quelle rimosse, mentre quelle ``+`` sono quelle aggiunte.\n--\nDocumentation/translations/it_IT/kernel-hacking/locking.rst-539-\nDocumentation/translations/it_IT/kernel-hacking/locking.rst:540: if ((obj = kmalloc_obj(*obj)) == NULL)\nDocumentation/translations/it_IT/kernel-hacking/locking.rst-541- return -ENOMEM;\n--\nDocumentation/translations/it_IT/locking/locktypes.rst=488=memoria. Su un kernel non-PREEMPT_RT il seguente codice funziona perfettamente::\n--\nDocumentation/translations/it_IT/locking/locktypes.rst-490- raw_spin_lock(\u0026lock);\nDocumentation/translations/it_IT/locking/locktypes.rst:491: p = kmalloc_obj(*p, GFP_ATOMIC);\nDocumentation/translations/it_IT/locking/locktypes.rst-492-\n--\nDocumentation/translations/it_IT/locking/locktypes.rst=497=PREEMPT_RT::\n--\nDocumentation/translations/it_IT/locking/locktypes.rst-499- spin_lock(\u0026lock);\nDocumentation/translations/it_IT/locking/locktypes.rst:500: p = kmalloc_obj(*p, GFP_ATOMIC);\nDocumentation/translations/it_IT/locking/locktypes.rst-501-\n--\nDocumentation/translations/it_IT/process/coding-style.rst=942=Il modo preferito per passare la dimensione di una struttura è il seguente:\n--\nDocumentation/translations/it_IT/process/coding-style.rst-945-\nDocumentation/translations/it_IT/process/coding-style.rst:946:\tp = kmalloc_obj(*p, ...);\nDocumentation/translations/it_IT/process/coding-style.rst-947-\n--\nDocumentation/translations/sp_SP/process/coding-style.rst=954=La forma preferida para pasar el tamaño de una estructura es la siguiente:\n--\nDocumentation/translations/sp_SP/process/coding-style.rst-957-\nDocumentation/translations/sp_SP/process/coding-style.rst:958:\tp = kmalloc_obj(*p, ...);\nDocumentation/translations/sp_SP/process/coding-style.rst-959-\n--\nDocumentation/translations/zh_CN/core-api/kref.rst=46=kref可以出现在数据结构体中的任何地方。\n--\nDocumentation/translations/zh_CN/core-api/kref.rst-54-\nDocumentation/translations/zh_CN/core-api/kref.rst:55: data = kmalloc_obj(*data);\nDocumentation/translations/zh_CN/core-api/kref.rst-56- if (!data)\n--\nDocumentation/translations/zh_CN/core-api/kref.rst=62=Kref规则\n--\nDocumentation/translations/zh_CN/core-api/kref.rst-108-\tstruct task_struct *task;\nDocumentation/translations/zh_CN/core-api/kref.rst:109:\tdata = kmalloc_obj(*data);\nDocumentation/translations/zh_CN/core-api/kref.rst-110-\tif (!data)\n--\nDocumentation/translations/zh_CN/process/coding-style.rst=810=Documentation/translations/zh_CN/core-api/memory-allocation.rst 。\n--\nDocumentation/translations/zh_CN/process/coding-style.rst-815-\nDocumentation/translations/zh_CN/process/coding-style.rst:816:\tp = kmalloc_obj(*p, ...);\nDocumentation/translations/zh_CN/process/coding-style.rst-817-\n--\nDocumentation/translations/zh_TW/process/coding-style.rst=824=Documentation/translations/zh_CN/core-api/memory-allocation.rst 。\n--\nDocumentation/translations/zh_TW/process/coding-style.rst-829-\nDocumentation/translations/zh_TW/process/coding-style.rst:830:\tp = kmalloc_obj(*p, ...);\nDocumentation/translations/zh_TW/process/coding-style.rst-831-\n--\narch/alpha/kernel/core_marvel.c=857=marvel_agp_setup(alpha_agp_info *agp)\n--\narch/alpha/kernel/core_marvel.c-863-\narch/alpha/kernel/core_marvel.c:864:\taper = kmalloc_obj(*aper);\narch/alpha/kernel/core_marvel.c-865-\tif (aper == NULL) return -ENOMEM;\n--\narch/alpha/kernel/core_marvel.c=1019=marvel_agp_info(void)\n--\narch/alpha/kernel/core_marvel.c-1061-\t */\narch/alpha/kernel/core_marvel.c:1062:\tagp = kmalloc_obj(*agp);\narch/alpha/kernel/core_marvel.c-1063-\tif (!agp)\n--\narch/alpha/kernel/core_titan.c=590=titan_agp_setup(alpha_agp_info *agp)\n--\narch/alpha/kernel/core_titan.c-596-\narch/alpha/kernel/core_titan.c:597:\taper = kmalloc_obj(struct titan_agp_aperture);\narch/alpha/kernel/core_titan.c-598-\tif (aper == NULL)\n--\narch/alpha/kernel/core_titan.c=731=titan_agp_info(void)\n--\narch/alpha/kernel/core_titan.c-762-\t */\narch/alpha/kernel/core_titan.c:763:\tagp = kmalloc_obj(*agp);\narch/alpha/kernel/core_titan.c-764-\tif (!agp)\n--\narch/alpha/kernel/module.c=29=process_reloc_for_got(Elf64_Rela *rela,\n--\narch/alpha/kernel/module.c-48-\narch/alpha/kernel/module.c:49:\tg = kmalloc_obj(*g);\narch/alpha/kernel/module.c-50-\tg-\u003enext = chains[r_sym].next;\n--\narch/alpha/kernel/pci.c=211=static void pdev_save_srm_config(struct pci_dev *dev)\n--\narch/alpha/kernel/pci.c-223-\narch/alpha/kernel/pci.c:224:\ttmp = kmalloc_obj(*tmp);\narch/alpha/kernel/pci.c-225-\tif (!tmp) {\n--\narch/arc/kernel/unwind.c=359=void *unwind_add_table(struct module *module, const void *table_start,\n--\narch/arc/kernel/unwind.c-368-\narch/arc/kernel/unwind.c:369:\ttable = kmalloc_obj(*table);\narch/arc/kernel/unwind.c-370-\tif (!table)\n--\narch/arm/common/locomo.c=274=static int locomo_suspend(struct platform_device *dev, pm_message_t state)\n--\narch/arm/common/locomo.c-279-\narch/arm/common/locomo.c:280:\tsave = kmalloc_obj(struct locomo_save_data);\narch/arm/common/locomo.c-281-\tif (!save)\n--\narch/arm/common/sa1111.c=964=static int sa1111_suspend_noirq(struct device *dev)\n--\narch/arm/common/sa1111.c-971-\narch/arm/common/sa1111.c:972:\tsave = kmalloc_obj(struct sa1111_save_data);\narch/arm/common/sa1111.c-973-\tif (!save)\n--\narch/arm/kernel/unwind.c=572=struct unwind_table *unwind_table_add(unsigned long start, unsigned long size,\n--\narch/arm/kernel/unwind.c-576-\tunsigned long flags;\narch/arm/kernel/unwind.c:577:\tstruct unwind_table *tab = kmalloc_obj(*tab);\narch/arm/kernel/unwind.c-578-\n--\narch/arm/mach-omap2/omap-iommu.c=53=static struct powerdomain *_get_pwrdm(struct device *dev)\n--\narch/arm/mach-omap2/omap-iommu.c-101-\narch/arm/mach-omap2/omap-iommu.c:102:\tentry = kmalloc_obj(*entry);\narch/arm/mach-omap2/omap-iommu.c-103-\tif (entry) {\n--\narch/arm/mach-omap2/pm34xx.c=406=static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)\n--\narch/arm/mach-omap2/pm34xx.c-412-\narch/arm/mach-omap2/pm34xx.c:413:\tpwrst = kmalloc_obj(struct power_state, GFP_ATOMIC);\narch/arm/mach-omap2/pm34xx.c-414-\tif (!pwrst)\n--\narch/arm/mach-omap2/pm44xx.c=113=static int __init pwrdms_setup(struct powerdomain *pwrdm, void *unused)\n--\narch/arm/mach-omap2/pm44xx.c-134-\narch/arm/mach-omap2/pm44xx.c:135:\tpwrst = kmalloc_obj(struct power_state, GFP_ATOMIC);\narch/arm/mach-omap2/pm44xx.c-136-\tif (!pwrst)\n--\narch/arm/mm/pgd.c-19-#ifdef CONFIG_ARM_LPAE\narch/arm/mm/pgd.c:20:#define _pgd_alloc(mm)\t\tkmalloc_objs(pgd_t, PTRS_PER_PGD, GFP_KERNEL | __GFP_ZERO)\narch/arm/mm/pgd.c-21-#define _pgd_free(mm, pgd)\tkfree(pgd)\n--\narch/arm/probes/kprobes/test-core.c=764=static int coverage_start(const union decode_item *table)\narch/arm/probes/kprobes/test-core.c-765-{\narch/arm/probes/kprobes/test-core.c:766:\tcoverage.base = kmalloc_objs(struct coverage_entry,\narch/arm/probes/kprobes/test-core.c-767-\t\t\t\t MAX_COVERAGE_ENTRIES);\n--\narch/arm64/kvm/pmu-emul.c=774=void kvm_host_pmu_init(struct arm_pmu *pmu)\n--\narch/arm64/kvm/pmu-emul.c-786-\narch/arm64/kvm/pmu-emul.c:787:\tentry = kmalloc_obj(*entry);\narch/arm64/kvm/pmu-emul.c-788-\tif (!entry)\n--\narch/arm64/kvm/vgic/vgic-debug.c=102=static void *vgic_debug_start(struct seq_file *s, loff_t *pos)\n--\narch/arm64/kvm/vgic/vgic-debug.c-106-\narch/arm64/kvm/vgic/vgic-debug.c:107:\titer = kmalloc_obj(*iter);\narch/arm64/kvm/vgic/vgic-debug.c-108-\tif (!iter)\n--\narch/arm64/kvm/vgic/vgic-debug.c=364=static void *vgic_its_debug_start(struct seq_file *s, loff_t *pos)\n--\narch/arm64/kvm/vgic/vgic-debug.c-377-\narch/arm64/kvm/vgic/vgic-debug.c:378:\titer = kmalloc_obj(*iter);\narch/arm64/kvm/vgic/vgic-debug.c-379-\tif (!iter)\n--\narch/arm64/kvm/vgic/vgic-init.c=743=void __init vgic_set_kvm_info(const struct gic_kvm_info *info)\n--\narch/arm64/kvm/vgic/vgic-init.c-745-\tBUG_ON(gic_kvm_info != NULL);\narch/arm64/kvm/vgic/vgic-init.c:746:\tgic_kvm_info = kmalloc_obj(*gic_kvm_info);\narch/arm64/kvm/vgic/vgic-init.c-747-\tif (gic_kvm_info)\n--\narch/m68k/emu/nfblock.c=97=static int __init nfhd_init_one(int id, u32 blocks, u32 bsize)\n--\narch/m68k/emu/nfblock.c-114-\narch/m68k/emu/nfblock.c:115:\tdev = kmalloc_obj(struct nfhd_device);\narch/m68k/emu/nfblock.c-116-\tif (!dev)\n--\narch/m68k/mm/kmap.c=108=static struct vm_struct *get_io_area(unsigned long size)\n--\narch/m68k/mm/kmap.c-112-\narch/m68k/mm/kmap.c:113:\tarea = kmalloc_obj(*area);\narch/m68k/mm/kmap.c-114-\tif (!area)\n--\narch/mips/alchemy/common/dbdma.c=253=u32 au1xxx_dbdma_chan_alloc(u32 srcid, u32 destid,\n--\narch/mips/alchemy/common/dbdma.c-312-\t\t\t */\narch/mips/alchemy/common/dbdma.c:313:\t\t\tctp = kmalloc_obj(chan_tab_t, GFP_ATOMIC);\narch/mips/alchemy/common/dbdma.c-314-\t\t\tchan_tab_ptr[i] = ctp;\n--\narch/mips/alchemy/common/dbdma.c=391=u32 au1xxx_dbdma_ring_alloc(u32 chanid, int entries)\n--\narch/mips/alchemy/common/dbdma.c-414-\t */\narch/mips/alchemy/common/dbdma.c:415:\tdesc_base = (u32) kmalloc_objs(au1x_ddma_desc_t, entries,\narch/mips/alchemy/common/dbdma.c-416-\t\t\t\t GFP_KERNEL | GFP_DMA);\n--\narch/mips/kernel/module.c=59=static int apply_r_mips_hi16(struct module *me, u32 *location, Elf_Addr v,\n--\narch/mips/kernel/module.c-74-\t */\narch/mips/kernel/module.c:75:\tn = kmalloc_obj(*n);\narch/mips/kernel/module.c-76-\tif (!n)\n--\narch/mips/kernel/vpe.c=311=static int apply_r_mips_hi16(struct module *me, uint32_t *location,\n--\narch/mips/kernel/vpe.c-320-\t */\narch/mips/kernel/vpe.c:321:\tn = kmalloc_obj(*n);\narch/mips/kernel/vpe.c-322-\tif (!n)\n--\narch/parisc/kernel/inventory.c=188=pat_query_module(ulong pcell_loc, ulong mod_index)\n--\narch/parisc/kernel/inventory.c-195-\narch/parisc/kernel/inventory.c:196:\tpa_pdc_cell = kmalloc_obj(*pa_pdc_cell);\narch/parisc/kernel/inventory.c-197-\tif (!pa_pdc_cell)\n--\narch/parisc/kernel/inventory.c=532=add_system_map_addresses(struct parisc_device *dev, int num_addrs, \n--\narch/parisc/kernel/inventory.c-538-\narch/parisc/kernel/inventory.c:539:\tdev-\u003eaddr = kmalloc_objs(*dev-\u003eaddr, num_addrs);\narch/parisc/kernel/inventory.c-540-\tif(!dev-\u003eaddr) {\n--\narch/parisc/kernel/processor.c=81=static int __init processor_probe(struct parisc_device *dev)\n--\narch/parisc/kernel/processor.c-112-\narch/parisc/kernel/processor.c:113:\t\tpa_pdc_cell = kmalloc_obj(*pa_pdc_cell);\narch/parisc/kernel/processor.c-114-\t\tif (!pa_pdc_cell)\n--\narch/parisc/kernel/unwind.c=149=unwind_table_add(const char *name, unsigned long base_addr, \n--\narch/parisc/kernel/unwind.c-159-\narch/parisc/kernel/unwind.c:160:\ttable = kmalloc_obj(struct unwind_table, GFP_USER);\narch/parisc/kernel/unwind.c-161-\tif (table == NULL)\n--\narch/parisc/kernel/unwind.c=406=void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t)\n--\narch/parisc/kernel/unwind.c-410-\narch/parisc/kernel/unwind.c:411:\tr2 = kmalloc_obj(struct pt_regs, GFP_ATOMIC);\narch/parisc/kernel/unwind.c-412-\tif (!r2)\n--\narch/powerpc/kernel/nvram_64.c=984=int __init nvram_scan_partitions(void)\n--\narch/powerpc/kernel/nvram_64.c-1032-\t\t}\narch/powerpc/kernel/nvram_64.c:1033:\t\ttmp_part = kmalloc_obj(*tmp_part);\narch/powerpc/kernel/nvram_64.c-1034-\t\terr = -ENOMEM;\n--\narch/powerpc/kvm/e500_mmu.c=731=int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu,\n--\narch/powerpc/kvm/e500_mmu.c-774-\t\t cfg-\u003earray / PAGE_SIZE;\narch/powerpc/kvm/e500_mmu.c:775:\tpages = kmalloc_objs(*pages, num_pages);\narch/powerpc/kvm/e500_mmu.c-776-\tif (!pages)\n--\narch/powerpc/kvm/e500_mmu.c=898=int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500)\n--\narch/powerpc/kvm/e500_mmu.c-914-\narch/powerpc/kvm/e500_mmu.c:915:\tvcpu_e500-\u003egtlb_arch = kmalloc_objs(*vcpu_e500-\u003egtlb_arch,\narch/powerpc/kvm/e500_mmu.c-916-\t\t\t\t\t KVM_E500_TLB0_SIZE + KVM_E500_TLB1_SIZE);\n--\narch/powerpc/lib/rheap.c=45=static int grow(rh_info_t * info, int max_blocks)\n--\narch/powerpc/lib/rheap.c-56-\narch/powerpc/lib/rheap.c:57:\tblock = kmalloc_objs(rh_block_t, max_blocks, GFP_ATOMIC);\narch/powerpc/lib/rheap.c-58-\tif (block == NULL)\n--\narch/powerpc/lib/rheap.c=253=rh_info_t *rh_create(unsigned int alignment)\n--\narch/powerpc/lib/rheap.c-260-\narch/powerpc/lib/rheap.c:261:\tinfo = kmalloc_obj(*info, GFP_ATOMIC);\narch/powerpc/lib/rheap.c-262-\tif (info == NULL)\n--\narch/powerpc/mm/book3s64/mmu_context.c=95=static int hash__init_new_context(struct mm_struct *mm)\n--\narch/powerpc/mm/book3s64/mmu_context.c-98-\narch/powerpc/mm/book3s64/mmu_context.c:99:\tmm-\u003econtext.hash_context = kmalloc_obj(struct hash_mm_context);\narch/powerpc/mm/book3s64/mmu_context.c-100-\tif (!mm-\u003econtext.hash_context)\n--\narch/powerpc/mm/book3s64/mmu_context.c-125-\t\tif (current-\u003emm-\u003econtext.hash_context-\u003espt) {\narch/powerpc/mm/book3s64/mmu_context.c:126:\t\t\tmm-\u003econtext.hash_context-\u003espt = kmalloc_obj(struct subpage_prot_table);\narch/powerpc/mm/book3s64/mmu_context.c-127-\t\t\tif (!mm-\u003econtext.hash_context-\u003espt) {\n--\narch/powerpc/perf/hv-24x7.c=623=static int event_uniq_add(struct rb_root *root, const char *name, int nl,\n--\narch/powerpc/perf/hv-24x7.c-650-\narch/powerpc/perf/hv-24x7.c:651:\tdata = kmalloc_obj(*data);\narch/powerpc/perf/hv-24x7.c-652-\tif (!data)\n--\narch/powerpc/perf/hv-24x7.c=755=static int create_events_from_catalog(struct attribute ***events_,\n--\narch/powerpc/perf/hv-24x7.c-908-\narch/powerpc/perf/hv-24x7.c:909:\tevents = kmalloc_objs(*events, attr_max + 1);\narch/powerpc/perf/hv-24x7.c-910-\tif (!events) {\n--\narch/powerpc/perf/hv-24x7.c-914-\narch/powerpc/perf/hv-24x7.c:915:\tevent_descs = kmalloc_objs(*event_descs, event_idx + 1);\narch/powerpc/perf/hv-24x7.c-916-\tif (!event_descs) {\n--\narch/powerpc/perf/hv-24x7.c-920-\narch/powerpc/perf/hv-24x7.c:921:\tevent_long_descs = kmalloc_objs(*event_long_descs, event_idx + 1);\narch/powerpc/perf/hv-24x7.c-922-\tif (!event_long_descs) {\n--\narch/powerpc/platforms/44x/hsta_msi.c=122=static int hsta_msi_probe(struct platform_device *pdev)\n--\narch/powerpc/platforms/44x/hsta_msi.c-153-\narch/powerpc/platforms/44x/hsta_msi.c:154:\tppc4xx_hsta_msi.irq_map = kmalloc_objs(int, irq_count);\narch/powerpc/platforms/44x/hsta_msi.c-155-\tif (!ppc4xx_hsta_msi.irq_map) {\n--\narch/powerpc/platforms/cell/spufs/file.c=44=static int spufs_attr_open(struct inode *inode, struct file *file,\n--\narch/powerpc/platforms/cell/spufs/file.c-49-\narch/powerpc/platforms/cell/spufs/file.c:50:\tattr = kmalloc_obj(*attr);\narch/powerpc/platforms/cell/spufs/file.c-51-\tif (!attr)\n--\narch/powerpc/platforms/pseries/dlpar.c=628=void queue_hotplug_event(struct pseries_hp_errorlog *hp_errlog)\n--\narch/powerpc/platforms/pseries/dlpar.c-636-\narch/powerpc/platforms/pseries/dlpar.c:637:\twork = kmalloc_obj(struct pseries_hp_work, GFP_ATOMIC);\narch/powerpc/platforms/pseries/dlpar.c-638-\tif (work) {\n--\narch/powerpc/platforms/pseries/hvcserver.c=119=int hvcs_get_partner_info(uint32_t unit_address, struct list_head *head,\n--\narch/powerpc/platforms/pseries/hvcserver.c-162-\t\t * hvcs_free_partner_info(). */\narch/powerpc/platforms/pseries/hvcserver.c:163:\t\tnext_partner_info = kmalloc_obj(struct hvcs_partner_info,\narch/powerpc/platforms/pseries/hvcserver.c-164-\t\t\t\t\t\tGFP_ATOMIC);\n--\narch/powerpc/platforms/pseries/lparcfg.c=144=static void show_gpci_data(struct seq_file *m)\n--\narch/powerpc/platforms/pseries/lparcfg.c-149-\narch/powerpc/platforms/pseries/lparcfg.c:150:\tbuf = kmalloc_obj(*buf);\narch/powerpc/platforms/pseries/lparcfg.c-151-\tif (buf == NULL)\n--\narch/powerpc/platforms/pseries/msi.c=435=static int pseries_msi_ops_prepare(struct irq_domain *domain, struct device *dev,\n--\narch/powerpc/platforms/pseries/msi.c-443-\tstruct pseries_msi_device *pseries_dev __free(kfree)\narch/powerpc/platforms/pseries/msi.c:444:\t\t= kmalloc_obj(*pseries_dev);\narch/powerpc/platforms/pseries/msi.c-445-\tif (!pseries_dev)\n--\narch/powerpc/platforms/pseries/pci.c=120=static int pseries_pci_sriov_enable(struct pci_dev *pdev, u16 num_vfs)\n--\narch/powerpc/platforms/pseries/pci.c-143-\tpdn = pci_get_pdn(pdev);\narch/powerpc/platforms/pseries/pci.c:144:\tpdn-\u003epe_num_map = kmalloc_objs(*pdn-\u003epe_num_map, num_vfs);\narch/powerpc/platforms/pseries/pci.c-145-\tif (!pdn-\u003epe_num_map)\n--\narch/powerpc/platforms/pseries/vas.c=1076=static int __init pseries_vas_init(void)\n--\narch/powerpc/platforms/pseries/vas.c-1089-\narch/powerpc/platforms/pseries/vas.c:1090:\thv_caps = kmalloc_obj(*hv_caps);\narch/powerpc/platforms/pseries/vas.c-1091-\tif (!hv_caps)\n--\narch/powerpc/platforms/pseries/vio.c=705=static int vio_cmo_bus_probe(struct vio_dev *viodev)\n--\narch/powerpc/platforms/pseries/vio.c-747-\narch/powerpc/platforms/pseries/vio.c:748:\t\tdev_ent = kmalloc_obj(struct vio_cmo_dev_entry);\narch/powerpc/platforms/pseries/vio.c-749-\t\tif (!dev_ent)\n--\narch/powerpc/sysdev/fsl_lbc.c=352=static int fsl_lbc_syscore_suspend(void *data)\n--\narch/powerpc/sysdev/fsl_lbc.c-364-\narch/powerpc/sysdev/fsl_lbc.c:365:\tctrl-\u003esaved_regs = kmalloc_obj(struct fsl_lbc_regs);\narch/powerpc/sysdev/fsl_lbc.c-366-\tif (!ctrl-\u003esaved_regs)\n--\narch/powerpc/sysdev/mpic.c=1549=void __init mpic_init(struct mpic *mpic)\n--\narch/powerpc/sysdev/mpic.c-1641-\t/* allocate memory to save mpic state */\narch/powerpc/sysdev/mpic.c:1642:\tmpic-\u003esave_data = kmalloc_objs(*mpic-\u003esave_data, mpic-\u003enum_sources);\narch/powerpc/sysdev/mpic.c-1643-\tBUG_ON(mpic-\u003esave_data == NULL);\n--\narch/powerpc/sysdev/of_rtc.c=23=void __init of_instantiate_rtc(void)\n--\narch/powerpc/sysdev/of_rtc.c-35-\narch/powerpc/sysdev/of_rtc.c:36:\t\t\tres = kmalloc_obj(*res);\narch/powerpc/sysdev/of_rtc.c-37-\t\t\tif (!res) {\n--\narch/riscv/kernel/module.c=654=static int add_relocation_to_accumulate(struct module *me, int type,\n--\narch/riscv/kernel/module.c-665-\narch/riscv/kernel/module.c:666:\tentry = kmalloc_obj(*entry);\narch/riscv/kernel/module.c-667-\n--\narch/riscv/kernel/module.c-699-\tif (!found) {\narch/riscv/kernel/module.c:700:\t\trel_head = kmalloc_obj(*rel_head);\narch/riscv/kernel/module.c-701-\n--\narch/riscv/kernel/module.c-711-\t\t\tbucket =\narch/riscv/kernel/module.c:712:\t\t\t\tkmalloc_obj(struct used_bucket);\n\n"
} |
| 73/3 | 2026/08/28 18:35 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 52489 output: 261 thoughts: 457 Thoughts: **My KMSAN Assessment** Okay, let's break this down. I'm looking at `kmalloc_obj(*bkey)`, so I'm allocating a `struct bpf_key` object. Crucially, the code *immediately* initializes both `key` and `has_ref` fields. That's a good start. Now, let's methodically check the KMSAN requirements. Remember, KMSAN is only needed if this patch touches something that could lead to uninitialized memory being used. Specifically, I need to check if this patch introduces or modifies: 1. Structures sent to userspace where initialization might be incomplete. 2. Conditional logic that relies on uninitialized data. 3. Complex data structure allocation where uninitialized fields could be read later. 4. Bounds checks that allow access to uninitialized bytes. Let's apply that to this patch: 1. This patch adds support for the `.bpf` keyring, involving `bpf.keyring_unsealed`, `KEY_SPEC_BPF_KEYRING`, and verifier checks. Okay. 2. The heap-allocated `struct bpf_key` in `bpf_lookup_keyring` explicitly sets both fields (`key` and `has_ref`). That's important; it's *initialized*. 3. The patch tightens the program signature size to `BPF_PROG_MAX_SIGNATURE_SIZE` (64KB) in `bpf_prog_verify_signature`. This impacts a *size*, not initialization. 4. There's no indication that any uninitialized memory is read, branched on, or copied to user space. 5. Any issues around key resolution, permissions, or signature verification, while important, are logic errors or memory corruption/lifetime problems. KASAN/fuzzing should catch those. Based on this analysis, I'm confident that this patch *doesn't* introduce any KMSAN concerns. Specifically, all relevant structures are initialized, and there's no risk of uninitialized memory being read, branched on, or exposed to userspace. Therefore, `NeedsKMSAN = false`. I'm ready to prepare the reasoning and call `set-results`. |
| 74/3 | 2026/08/28 18:35 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series adds support for the `.bpf` keyring (via the `bpf.keyring_unsealed` boot parameter, `KEY_SPEC_BPF_KEYRING` ID, and signature verifier integration) and tightens the maximum signature size check for BPF programs.\n\nAnalysis of uninitialized memory risk:\n1. No uninitialized data or struct padding is copied to userspace via `copy_to_user`, BPF maps, or syscall attributes.\n2. The newly introduced `struct bpf_key` allocation in `bpf_lookup_keyring()` explicitly initializes both of its struct fields (`key` and `has_ref`) prior to returning.\n3. No conditional branches rely on uninitialized stack or heap state.\n4. Signature verification operates on memory copied cleanly with `kvmemdup_bpfptr()`.\n\nAny potential bugs related to memory safety, pointer validation, or reference counting are standard lifetime or memory safety issues caught by KASAN/standard detectors. There are no KMSAN-specific uninitialized memory or info-leak risks that justify a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series adds support for the `.bpf` keyring (via the `bpf.keyring_unsealed` boot parameter, `KEY_SPEC_BPF_KEYRING` ID, and signature verifier integration) and tightens the maximum signature size check for BPF programs.\n\nAnalysis of uninitialized memory risk:\n1. No uninitialized data or struct padding is copied to userspace via `copy_to_user`, BPF maps, or syscall attributes.\n2. The newly introduced `struct bpf_key` allocation in `bpf_lookup_keyring()` explicitly initializes both of its struct fields (`key` and `has_ref`) prior to returning.\n3. No conditional branches rely on uninitialized stack or heap state.\n4. Signature verification operates on memory copied cleanly with `kvmemdup_bpfptr()`.\n\nAny potential bugs related to memory safety, pointer validation, or reference counting are standard lifetime or memory safety issues caught by KASAN/standard detectors. There are no KMSAN-specific uninitialized memory or info-leak risks that justify a dedicated KMSAN fuzzing session.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|