binfmt_script.c (4291B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * linux/fs/binfmt_script.c 4 * 5 * Copyright (C) 1996 Martin von Löwis 6 * original #!-checking implemented by tytso. 7 */ 8 9#include <linux/module.h> 10#include <linux/string.h> 11#include <linux/stat.h> 12#include <linux/binfmts.h> 13#include <linux/init.h> 14#include <linux/file.h> 15#include <linux/err.h> 16#include <linux/fs.h> 17 18static inline bool spacetab(char c) { return c == ' ' || c == '\t'; } 19static inline const char *next_non_spacetab(const char *first, const char *last) 20{ 21 for (; first <= last; first++) 22 if (!spacetab(*first)) 23 return first; 24 return NULL; 25} 26static inline const char *next_terminator(const char *first, const char *last) 27{ 28 for (; first <= last; first++) 29 if (spacetab(*first) || !*first) 30 return first; 31 return NULL; 32} 33 34static int load_script(struct linux_binprm *bprm) 35{ 36 const char *i_name, *i_sep, *i_arg, *i_end, *buf_end; 37 struct file *file; 38 int retval; 39 40 /* Not ours to exec if we don't start with "#!". */ 41 if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!')) 42 return -ENOEXEC; 43 44 /* 45 * This section handles parsing the #! line into separate 46 * interpreter path and argument strings. We must be careful 47 * because bprm->buf is not yet guaranteed to be NUL-terminated 48 * (though the buffer will have trailing NUL padding when the 49 * file size was smaller than the buffer size). 50 * 51 * We do not want to exec a truncated interpreter path, so either 52 * we find a newline (which indicates nothing is truncated), or 53 * we find a space/tab/NUL after the interpreter path (which 54 * itself may be preceded by spaces/tabs). Truncating the 55 * arguments is fine: the interpreter can re-read the script to 56 * parse them on its own. 57 */ 58 buf_end = bprm->buf + sizeof(bprm->buf) - 1; 59 i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n'); 60 if (!i_end) { 61 i_end = next_non_spacetab(bprm->buf + 2, buf_end); 62 if (!i_end) 63 return -ENOEXEC; /* Entire buf is spaces/tabs */ 64 /* 65 * If there is no later space/tab/NUL we must assume the 66 * interpreter path is truncated. 67 */ 68 if (!next_terminator(i_end, buf_end)) 69 return -ENOEXEC; 70 i_end = buf_end; 71 } 72 /* Trim any trailing spaces/tabs from i_end */ 73 while (spacetab(i_end[-1])) 74 i_end--; 75 76 /* Skip over leading spaces/tabs */ 77 i_name = next_non_spacetab(bprm->buf+2, i_end); 78 if (!i_name || (i_name == i_end)) 79 return -ENOEXEC; /* No interpreter name found */ 80 81 /* Is there an optional argument? */ 82 i_arg = NULL; 83 i_sep = next_terminator(i_name, i_end); 84 if (i_sep && (*i_sep != '\0')) 85 i_arg = next_non_spacetab(i_sep, i_end); 86 87 /* 88 * If the script filename will be inaccessible after exec, typically 89 * because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give 90 * up now (on the assumption that the interpreter will want to load 91 * this file). 92 */ 93 if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE) 94 return -ENOENT; 95 96 /* 97 * OK, we've parsed out the interpreter name and 98 * (optional) argument. 99 * Splice in (1) the interpreter's name for argv[0] 100 * (2) (optional) argument to interpreter 101 * (3) filename of shell script (replace argv[0]) 102 * 103 * This is done in reverse order, because of how the 104 * user environment and arguments are stored. 105 */ 106 retval = remove_arg_zero(bprm); 107 if (retval) 108 return retval; 109 retval = copy_string_kernel(bprm->interp, bprm); 110 if (retval < 0) 111 return retval; 112 bprm->argc++; 113 *((char *)i_end) = '\0'; 114 if (i_arg) { 115 *((char *)i_sep) = '\0'; 116 retval = copy_string_kernel(i_arg, bprm); 117 if (retval < 0) 118 return retval; 119 bprm->argc++; 120 } 121 retval = copy_string_kernel(i_name, bprm); 122 if (retval) 123 return retval; 124 bprm->argc++; 125 retval = bprm_change_interp(i_name, bprm); 126 if (retval < 0) 127 return retval; 128 129 /* 130 * OK, now restart the process with the interpreter's dentry. 131 */ 132 file = open_exec(i_name); 133 if (IS_ERR(file)) 134 return PTR_ERR(file); 135 136 bprm->interpreter = file; 137 return 0; 138} 139 140static struct linux_binfmt script_format = { 141 .module = THIS_MODULE, 142 .load_binary = load_script, 143}; 144 145static int __init init_script_binfmt(void) 146{ 147 register_binfmt(&script_format); 148 return 0; 149} 150 151static void __exit exit_script_binfmt(void) 152{ 153 unregister_binfmt(&script_format); 154} 155 156core_initcall(init_script_binfmt); 157module_exit(exit_script_binfmt); 158MODULE_LICENSE("GPL");