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 | |
18 | static inline bool spacetab(char c) { return c == ' ' || c == '\t'; } |
19 | static inline const char *next_non_spacetab(const char *first, const char *last) |
20 | { |
21 | for (; first <= last; first++) |
22 | if (!spacetab(c: *first)) |
23 | return first; |
24 | return NULL; |
25 | } |
26 | static inline const char *next_terminator(const char *first, const char *last) |
27 | { |
28 | for (; first <= last; first++) |
29 | if (spacetab(c: *first) || !*first) |
30 | return first; |
31 | return NULL; |
32 | } |
33 | |
34 | static 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(first: bprm->buf + 2, last: 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(first: i_end, last: buf_end)) |
69 | return -ENOEXEC; |
70 | i_end = buf_end; |
71 | } |
72 | /* Trim any trailing spaces/tabs from i_end */ |
73 | while (spacetab(c: i_end[-1])) |
74 | i_end--; |
75 | |
76 | /* Skip over leading spaces/tabs */ |
77 | i_name = next_non_spacetab(first: bprm->buf+2, last: 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(first: i_name, last: i_end); |
84 | if (i_sep && (*i_sep != '\0')) |
85 | i_arg = next_non_spacetab(first: i_sep, last: 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(arg: 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(arg: i_arg, bprm); |
117 | if (retval < 0) |
118 | return retval; |
119 | bprm->argc++; |
120 | } |
121 | retval = copy_string_kernel(arg: i_name, bprm); |
122 | if (retval) |
123 | return retval; |
124 | bprm->argc++; |
125 | retval = bprm_change_interp(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(ptr: file)) |
134 | return PTR_ERR(ptr: file); |
135 | |
136 | bprm->interpreter = file; |
137 | return 0; |
138 | } |
139 | |
140 | static struct linux_binfmt script_format = { |
141 | .module = THIS_MODULE, |
142 | .load_binary = load_script, |
143 | }; |
144 | |
145 | static int __init init_script_binfmt(void) |
146 | { |
147 | register_binfmt(fmt: &script_format); |
148 | return 0; |
149 | } |
150 | |
151 | static void __exit exit_script_binfmt(void) |
152 | { |
153 | unregister_binfmt(&script_format); |
154 | } |
155 | |
156 | core_initcall(init_script_binfmt); |
157 | module_exit(exit_script_binfmt); |
158 | MODULE_LICENSE("GPL" ); |
159 | |