summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Brauner <brauner@kernel.org>2026-09-18 11:20:59 +0200
committerChristian Brauner <brauner@kernel.org>2026-09-18 11:20:59 +0200
commitf615c80a5d3e16eee2377c63e20ae79038ae3376 (patch)
treeedf1d5a2473e3e2e44fc8f0291a5e6bc2313186f
parent5179241521401ef364294128bb43cdbce7252457 (diff)
parent1970fc4ecb52dabce2e57f9be721964b936a052d (diff)
downloadlinux-next-f615c80a5d3e16eee2377c63e20ae79038ae3376.tar.gz
linux-next-f615c80a5d3e16eee2377c63e20ae79038ae3376.zip
Merge patch series "binfmt: fixes for kres reports"
Christian Brauner <brauner@kernel.org> says: I asked Chris to run his kres tooling on the binfmt with bpf changes merged for this cycle. It found two issues that are fixed in this series. I reproduced both of them. * patches from https://patch.msgid.link/20260918-work-binfmt_misc-fixes-v1-0-647b24bc1c46@kernel.org: binfmt_misc: fix racy checks in bpf set_interp kfuncs binfmt_misc: fix OOB read in bpf_binprm_select_interp() Link: https://patch.msgid.link/20260918-work-binfmt_misc-fixes-v1-0-647b24bc1c46@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
-rw-r--r--fs/binfmt_misc_bpf.c35
1 files changed, 32 insertions, 3 deletions
diff --git a/fs/binfmt_misc_bpf.c b/fs/binfmt_misc_bpf.c
index 91576ff05911..a3e26e8a4027 100644
--- a/fs/binfmt_misc_bpf.c
+++ b/fs/binfmt_misc_bpf.c
@@ -141,8 +141,6 @@ __bpf_kfunc int bpf_binprm_set_interp(struct linux_binprm *bprm,
len = strnlen(path, path__sz);
if (len == path__sz)
return -EINVAL;
- if (path[0] != '/')
- return -EINVAL;
if (len >= PATH_MAX)
return -ENAMETOOLONG;
@@ -150,6 +148,15 @@ __bpf_kfunc int bpf_binprm_set_interp(struct linux_binprm *bprm,
if (!interp)
return -ENOMEM;
+ /*
+ * The program may pass memory that is written to while this runs,
+ * so check the private copy and not the buffer it was made from.
+ */
+ if (interp[0] != '/') {
+ kfree(interp);
+ return -EINVAL;
+ }
+
bm_bpf_stage_selection(bprm, interp, NULL);
return 0;
}
@@ -176,6 +183,7 @@ __bpf_kfunc int bpf_binprm_select_interp(struct linux_binprm *bprm,
const char *name, size_t name__sz)
{
const struct binfmt_misc_interp *interp;
+ char buf[BINFMT_MISC_INTERP_NAME_MAX + 1];
size_t len;
char *path;
@@ -184,8 +192,20 @@ __bpf_kfunc int bpf_binprm_select_interp(struct linux_binprm *bprm,
len = strnlen(name, name__sz);
if (len == name__sz || !len)
return -EINVAL;
+ /* No entry binds a longer name, so it cannot be found. */
+ if (len > BINFMT_MISC_INTERP_NAME_MAX)
+ return -ENOENT;
+
+ /*
+ * The program may pass memory that is written to while this runs,
+ * so look the name up in a private copy and check that instead.
+ */
+ memcpy(buf, name, len);
+ buf[len] = '\0';
+ if (!buf[0])
+ return -EINVAL;
- interp = binfmt_misc_find_interp(bprm->bpf_interps, name);
+ interp = binfmt_misc_find_interp(bprm->bpf_interps, buf);
if (!interp)
return -ENOENT;
@@ -228,6 +248,15 @@ __bpf_kfunc int bpf_binprm_set_interp_arg(struct linux_binprm *bprm,
if (!val)
return -ENOMEM;
+ /*
+ * The program may pass memory that is written to while this runs,
+ * so check the private copy and not the buffer it was made from.
+ */
+ if (!val[0]) {
+ kfree(val);
+ return -EINVAL;
+ }
+
kfree(bprm->bpf_interp_arg);
bprm->bpf_interp_arg = val;
return 0;