Re: [PATCH v3] exec: load_script: Do not exec truncated interpreter path

From: Linus Torvalds
Date: Fri Feb 15 2019 - 10:55:12 EST


On Thu, Feb 14, 2019 at 10:15 PM Kees Cook <keescook@xxxxxxxxxxxx> wrote:
>
> The only way we know the interpreter wasn't truncated in the
> no-newline case is if we see whitespace after first skipping any
> leading whitespace, and it seemed really ugly to add a special scan
> there.

No, much easier (and likely better code too), to just use 'memchr()'.

What's wrong with this simple and fairly self-describing patch?

And I'll rather add a few lines due to helper functions with names to
make it more legible, rather than code in that already fairly long
existing function.

Linus
fs/binfmt_script.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index 7cde3f46ad26..c9a7afd477a9 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -14,6 +14,24 @@
#include <linux/err.h>
#include <linux/fs.h>

+/*
+ * Do we have whitespace (ignoring leading) between first and last
+ * (inclusive). This is the "we are truncating the script command
+ * line" case.
+ */
+static inline bool tabspc(char c) { return c == ' ' || c == '\t'; }
+static inline bool no_tab_or_space(const char *first, const char *last)
+{
+ /* Skip leading space (and first non-space) */
+ while (tabspc(*first++))
+ if (first > last)
+ return false;
+ while (first <= last)
+ if (tabspc(*first++))
+ return true;
+ return false;
+}
+
static int load_script(struct linux_binprm *bprm)
{
const char *i_arg, *i_name;
@@ -42,9 +60,12 @@ static int load_script(struct linux_binprm *bprm)
fput(bprm->file);
bprm->file = NULL;

- bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
- if ((cp = strchr(bprm->buf, '\n')) == NULL)
+ cp = memchr(bprm->buf, BINPRM_BUF_SIZE, '\n');
+ if (!cp) {
cp = bprm->buf+BINPRM_BUF_SIZE-1;
+ if (no_tab_or_space(bprm->buf+2, cp))
+ return -ENOEXEC;
+ }
*cp = '\0';
while (cp > bprm->buf) {
cp--;