diff options
| author | Christian Brauner <brauner@kernel.org> | 2026-09-18 11:20:59 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-09-18 11:20:59 +0200 |
| commit | f615c80a5d3e16eee2377c63e20ae79038ae3376 (patch) | |
| tree | edf1d5a2473e3e2e44fc8f0291a5e6bc2313186f | |
| parent | 5179241521401ef364294128bb43cdbce7252457 (diff) | |
| parent | 1970fc4ecb52dabce2e57f9be721964b936a052d (diff) | |
| download | linux-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.c | 35 |
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; |
