fs/binfmt_script.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c index 7cde3f46ad26..41be15701383 100644 --- a/fs/binfmt_script.c +++ b/fs/binfmt_script.c @@ -14,6 +14,29 @@ #include #include +/* + * Do we have a terminating character between 'first' and 'last' + * (inclusive). This is the "we are truncating the script command + * line" case, and we know first < last. + * + * We skip leading whitespace, and then verify there's a space/tab + * or NUL before the end. + */ +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 + for (;tabspc(*first) ; first++) + if (!*first || first == last) + return false; // only space + // Ok, 'first' points to first non-spc/tab/NUL + // Can we find another terminator after this? + while (++first <= last) + if (!*first || tabspc(*first)) + return true; + return false; +} + static int load_script(struct linux_binprm *bprm) { const char *i_arg, *i_name; @@ -42,9 +65,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 = strnchr(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--;