From: Song Liu <song@kernel.org>
To: bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-security-module@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org,
kernel-team@meta.com, viro@zeniv.linux.org.uk,
brauner@kernel.org, jack@suse.cz, paul@paul-moore.com,
jmorris@namei.org, serge@hallyn.com, Song Liu <song@kernel.org>
Subject: [PATCH bpf-next 1/3] bpf: Allow const char * from LSM hooks as kfunc const string arguments
Date: Wed, 26 Nov 2025 16:50:09 -0800 [thread overview]
Message-ID: <20251127005011.1872209-6-song@kernel.org> (raw)
In-Reply-To: <20251127005011.1872209-1-song@kernel.org>
Let the BPF verifier to recognize const char * arguments from LSM hooks
(and other BPF program types) as valid const string pointers that can be
passed to kfuncs expecting KF_ARG_PTR_TO_CONST_STR.
Previously, kfuncs with KF_ARG_PTR_TO_CONST_STR only accepted
PTR_TO_MAP_VALUE from readonly maps. This was limiting for LSM programs
that receive const char * arguments from hooks like sb_mount's dev_name.
Signed-off-by: Song Liu <song@kernel.org>
---
include/linux/btf.h | 1 +
kernel/bpf/btf.c | 33 ++++++++++++++++++++++++++++
kernel/bpf/verifier.c | 51 +++++++++++++++++++++++++++++++++----------
3 files changed, 73 insertions(+), 12 deletions(-)
diff --git a/include/linux/btf.h b/include/linux/btf.h
index f06976ffb63f..bd5a32d33254 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -224,6 +224,7 @@ struct btf *btf_base_btf(const struct btf *btf);
bool btf_type_is_i32(const struct btf_type *t);
bool btf_type_is_i64(const struct btf_type *t);
bool btf_type_is_primitive(const struct btf_type *t);
+bool btf_type_is_const_char_ptr(const struct btf *btf, const struct btf_type *t);
bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
const struct btf_member *m,
u32 expected_offset, u32 expected_size);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 0de8fc8a0e0b..94a272585b97 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -897,6 +897,25 @@ bool btf_type_is_primitive(const struct btf_type *t)
btf_is_any_enum(t);
}
+bool btf_type_is_const_char_ptr(const struct btf *btf, const struct btf_type *t)
+{
+ const char *tname;
+
+ /* The type chain has to be PTR->CONST->CHAR */
+ if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR)
+ return false;
+
+ t = btf_type_by_id(btf, t->type);
+ if (BTF_INFO_KIND(t->info) != BTF_KIND_CONST)
+ return false;
+
+ t = btf_type_by_id(btf, t->type);
+ tname = btf_name_by_offset(btf, t->name_off);
+ if (tname && strcmp(tname, "char") == 0)
+ return true;
+ return false;
+}
+
/*
* Check that given struct member is a regular int with expected
* offset and size.
@@ -6746,6 +6765,20 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
/* Default prog with MAX_BPF_FUNC_REG_ARGS args */
return true;
t = btf_type_by_id(btf, args[arg].type);
+
+ /*
+ * For const string, we need to match "const char *"
+ * exactly. Therefore, do the check before the skipping
+ * modifiers.
+ */
+ if (btf_type_is_const_char_ptr(btf, t)) {
+ info->reg_type = PTR_TO_BTF_ID;
+ if (prog_args_trusted(prog))
+ info->reg_type |= PTR_TRUSTED;
+ info->btf = btf;
+ info->btf_id = args[arg].type;
+ return true;
+ }
}
/* skip modifiers */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 766695491bc5..a9757c056d4b 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9598,8 +9598,12 @@ static enum bpf_dynptr_type dynptr_get_type(struct bpf_verifier_env *env,
return state->stack[spi].spilled_ptr.dynptr.type;
}
-static int check_reg_const_str(struct bpf_verifier_env *env,
- struct bpf_reg_state *reg, u32 regno)
+/*
+ * Check for const string saved in a bpf map. The caller is responsible
+ * to check reg->type == PTR_TO_MAP_VALUE.
+ */
+static int check_reg_const_str_in_map(struct bpf_verifier_env *env,
+ struct bpf_reg_state *reg, u32 regno)
{
struct bpf_map *map = reg->map_ptr;
int err;
@@ -9607,9 +9611,6 @@ static int check_reg_const_str(struct bpf_verifier_env *env,
u64 map_addr;
char *str_ptr;
- if (reg->type != PTR_TO_MAP_VALUE)
- return -EINVAL;
-
if (!bpf_map_is_rdonly(map)) {
verbose(env, "R%d does not point to a readonly map'\n", regno);
return -EACCES;
@@ -9646,6 +9647,26 @@ static int check_reg_const_str(struct bpf_verifier_env *env,
return 0;
}
+/* Check for const string passed in as input to the bpf program. */
+static int check_reg_const_str_arg(struct bpf_reg_state *reg)
+{
+ const struct btf *btf;
+ const struct btf_type *t;
+ const char *tname;
+
+ if (base_type(reg->type) != PTR_TO_BTF_ID)
+ return -EINVAL;
+
+ btf = reg->btf;
+ t = btf_type_by_id(btf, reg->btf_id);
+ if (!t)
+ return -EINVAL;
+
+ if (btf_type_is_const_char_ptr(btf, t))
+ return 0;
+ return -EINVAL;
+}
+
/* Returns constant key value in `value` if possible, else negative error */
static int get_constant_map_key(struct bpf_verifier_env *env,
struct bpf_reg_state *key,
@@ -9964,7 +9985,9 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg,
break;
case ARG_PTR_TO_CONST_STR:
{
- err = check_reg_const_str(env, reg, regno);
+ if (reg->type != PTR_TO_MAP_VALUE)
+ return -EINVAL;
+ err = check_reg_const_str_in_map(env, reg, regno);
if (err)
return err;
break;
@@ -13626,13 +13649,17 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
meta->arg_btf_id = reg->btf_id;
break;
case KF_ARG_PTR_TO_CONST_STR:
- if (reg->type != PTR_TO_MAP_VALUE) {
- verbose(env, "arg#%d doesn't point to a const string\n", i);
- return -EINVAL;
+ if (reg->type == PTR_TO_MAP_VALUE) {
+ ret = check_reg_const_str_in_map(env, reg, regno);
+ if (ret)
+ return ret;
+ } else {
+ ret = check_reg_const_str_arg(reg);
+ if (ret) {
+ verbose(env, "arg#%d doesn't point to a const string\n", i);
+ return ret;
+ }
}
- ret = check_reg_const_str(env, reg, regno);
- if (ret)
- return ret;
break;
case KF_ARG_PTR_TO_WORKQUEUE:
if (reg->type != PTR_TO_MAP_VALUE) {
--
2.47.3
next prev parent reply other threads:[~2025-11-27 0:50 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-27 0:50 [PATCH bpf-next 0/3] Introduce bpf_kern_path and bpf_path_put Song Liu
2025-11-27 0:50 ` [PATCH bpf-next 1/3] bpf: Allow const char * from LSM hooks as kfunc const string arguments Song Liu
2025-11-27 0:50 ` [PATCH bpf-next 2/3] bpf: Add bpf_kern_path and bpf_path_put kfuncs Song Liu
2025-11-30 4:23 ` Al Viro
2025-11-30 5:57 ` Song Liu
2025-11-30 6:46 ` Al Viro
2025-12-01 7:32 ` Song Liu
2025-11-27 0:50 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for bpf_kern_path kfunc Song Liu
2025-11-27 0:50 ` [PATCH bpf-next 0/3] Introduce bpf_kern_path and bpf_path_put Song Liu
2025-11-27 0:50 ` Song Liu [this message]
2025-11-27 19:07 ` [PATCH bpf-next 1/3] bpf: Allow const char * from LSM hooks as kfunc const string arguments kernel test robot
2025-11-27 0:50 ` [PATCH bpf-next 2/3] bpf: Add bpf_kern_path and bpf_path_put kfuncs Song Liu
2025-11-27 0:50 ` [PATCH bpf-next 3/3] selftests/bpf: Add tests for bpf_kern_path kfunc Song Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251127005011.1872209-6-song@kernel.org \
--to=song@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=daniel@iogearbox.net \
--cc=jack@suse.cz \
--cc=jmorris@namei.org \
--cc=kernel-team@meta.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.