Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/kern/imgact_shell.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 1993, David Greenman
5
* All rights reserved.
6
*
7
* Redistribution and use in source and binary forms, with or without
8
* modification, are permitted provided that the following conditions
9
* are met:
10
* 1. Redistributions of source code must retain the above copyright
11
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
15
*
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
* SUCH DAMAGE.
27
*/
28
29
#include <sys/param.h>
30
#include <sys/vnode.h>
31
#include <sys/proc.h>
32
#include <sys/sbuf.h>
33
#include <sys/systm.h>
34
#include <sys/sysproto.h>
35
#include <sys/exec.h>
36
#include <sys/imgact.h>
37
#include <sys/kernel.h>
38
39
#if BYTE_ORDER == LITTLE_ENDIAN
40
#define SHELLMAGIC 0x2123 /* #! */
41
#else
42
#define SHELLMAGIC 0x2321
43
#endif
44
45
/*
46
* At the time of this writing, MAXSHELLCMDLEN == PAGE_SIZE. This is
47
* significant because the caller has only mapped in one page of the
48
* file we're reading.
49
*/
50
#if MAXSHELLCMDLEN > PAGE_SIZE
51
#error "MAXSHELLCMDLEN is larger than a single page!"
52
#endif
53
54
/*
55
* MAXSHELLCMDLEN must be at least MAXINTERP plus the size of the `#!'
56
* prefix and terminating newline.
57
*/
58
CTASSERT(MAXSHELLCMDLEN >= MAXINTERP + 3);
59
60
/**
61
* Shell interpreter image activator. An interpreter name beginning at
62
* imgp->args->begin_argv is the minimal successful exit requirement.
63
*
64
* If the given file is a shell-script, then the first line will start
65
* with the two characters `#!' (aka SHELLMAGIC), followed by the name
66
* of the shell-interpreter to run, followed by zero or more tokens.
67
*
68
* The interpreter is then started up such that it will see:
69
* arg[0] -> The name of interpreter as specified after `#!' in the
70
* first line of the script. The interpreter name must
71
* not be longer than MAXSHELLCMDLEN bytes.
72
* arg[1] -> *If* there are any additional tokens on the first line,
73
* then we add a new arg[1], which is a copy of the rest of
74
* that line. The copy starts at the first token after the
75
* interpreter name. We leave it to the interpreter to
76
* parse the tokens in that value.
77
* arg[x] -> the full pathname of the script. This will either be
78
* arg[2] or arg[1], depending on whether or not tokens
79
* were found after the interpreter name.
80
* arg[x+1] -> all the arguments that were specified on the original
81
* command line.
82
*
83
* This processing is described in the execve(2) man page.
84
*/
85
86
/*
87
* HISTORICAL NOTE: From 1993 to mid-2005, FreeBSD parsed out the tokens as
88
* found on the first line of the script, and setup each token as a separate
89
* value in arg[]. This extra processing did not match the behavior of other
90
* OS's, and caused a few subtle problems. For one, it meant the kernel was
91
* deciding how those values should be parsed (wrt characters for quoting or
92
* comments, etc), while the interpreter might have other rules for parsing.
93
* It also meant the interpreter had no way of knowing which arguments came
94
* from the first line of the shell script, and which arguments were specified
95
* by the user on the command line. That extra processing was dropped in the
96
* 6.x branch on May 28, 2005 (matching __FreeBSD_version 600029).
97
*/
98
int
99
exec_shell_imgact(struct image_params *imgp)
100
{
101
const char *image_header = imgp->image_header;
102
const char *ihp, *interpb, *interpe, *maxp, *optb, *opte, *fname;
103
int error, offset;
104
size_t length;
105
struct vattr vattr;
106
struct sbuf *sname;
107
108
/* a shell script? */
109
if (((const short *)image_header)[0] != SHELLMAGIC)
110
return (-1);
111
112
/*
113
* Don't allow a shell script to be the shell for a shell
114
* script. :-)
115
*/
116
if (imgp->interpreted & IMGACT_SHELL)
117
return (ENOEXEC);
118
119
imgp->interpreted |= IMGACT_SHELL;
120
121
/*
122
* At this point we have the first page of the file mapped.
123
* However, we don't know how far into the page the contents are
124
* valid -- the actual file might be much shorter than the page.
125
* So find out the file size.
126
*/
127
error = VOP_GETATTR(imgp->vp, &vattr, imgp->proc->p_ucred);
128
if (error)
129
return (error);
130
131
/*
132
* Copy shell name and arguments from image_header into a string
133
* buffer.
134
*/
135
maxp = &image_header[MIN(vattr.va_size, MAXSHELLCMDLEN)];
136
ihp = &image_header[2];
137
138
/*
139
* Find the beginning and end of the interpreter_name. If the
140
* line does not include any interpreter, or if the name which
141
* was found is too long, we bail out.
142
*/
143
while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))
144
ihp++;
145
interpb = ihp;
146
while (ihp < maxp && ((*ihp != ' ') && (*ihp != '\t') && (*ihp != '\n')
147
&& (*ihp != '\0')))
148
ihp++;
149
interpe = ihp;
150
if (interpb == interpe)
151
return (ENOEXEC);
152
if (interpe - interpb >= MAXINTERP)
153
return (ENAMETOOLONG);
154
155
/*
156
* Find the beginning of the options (if any), and the end-of-line.
157
* Then trim the trailing blanks off the value. Note that some
158
* other operating systems do *not* trim the trailing whitespace...
159
*/
160
while (ihp < maxp && ((*ihp == ' ') || (*ihp == '\t')))
161
ihp++;
162
optb = ihp;
163
while (ihp < maxp && ((*ihp != '\n') && (*ihp != '\0')))
164
ihp++;
165
opte = ihp;
166
if (opte == maxp)
167
return (ENOEXEC);
168
while (--ihp > optb && ((*ihp == ' ') || (*ihp == '\t')))
169
opte = ihp;
170
171
if (imgp->args->fname != NULL) {
172
fname = imgp->args->fname;
173
sname = NULL;
174
} else {
175
sname = sbuf_new_auto();
176
sbuf_printf(sname, "/dev/fd/%d", imgp->args->fd);
177
sbuf_finish(sname);
178
fname = sbuf_data(sname);
179
}
180
181
/*
182
* We need to "pop" (remove) the present value of arg[0], and "push"
183
* either two or three new values in the arg[] list. To do this,
184
* we first shift all the other values in the `begin_argv' area to
185
* provide the exact amount of room for the values added. Set up
186
* `offset' as the number of bytes to be added to the `begin_argv'
187
* area, and 'length' as the number of bytes being removed.
188
*/
189
offset = interpe - interpb + 1; /* interpreter */
190
if (opte > optb) /* options (if any) */
191
offset += opte - optb + 1;
192
offset += strlen(fname) + 1; /* fname of script */
193
length = (imgp->args->argc == 0) ? 0 :
194
strlen(imgp->args->begin_argv) + 1; /* bytes to delete */
195
196
error = exec_args_adjust_args(imgp->args, length, offset);
197
if (error != 0) {
198
if (sname != NULL)
199
sbuf_delete(sname);
200
return (error);
201
}
202
203
/*
204
* If there was no arg[0] when we started, then the interpreter_name
205
* is adding an argument (instead of replacing the arg[0] we started
206
* with). And we're always adding an argument when we include the
207
* full pathname of the original script.
208
*/
209
if (imgp->args->argc == 0)
210
imgp->args->argc = 1;
211
imgp->args->argc++;
212
213
/*
214
* The original arg[] list has been shifted appropriately. Copy in
215
* the interpreter name and options-string.
216
*/
217
length = interpe - interpb;
218
bcopy(interpb, imgp->args->begin_argv, length);
219
*(imgp->args->begin_argv + length) = '\0';
220
offset = length + 1;
221
if (opte > optb) {
222
length = opte - optb;
223
bcopy(optb, imgp->args->begin_argv + offset, length);
224
*(imgp->args->begin_argv + offset + length) = '\0';
225
offset += length + 1;
226
imgp->args->argc++;
227
}
228
229
/*
230
* Finally, add the filename onto the end for the interpreter to
231
* use and copy the interpreter's name to imgp->interpreter_name
232
* for exec to use.
233
*/
234
error = copystr(fname, imgp->args->begin_argv + offset,
235
imgp->args->stringspace, NULL);
236
237
if (error == 0)
238
imgp->interpreter_name = imgp->args->begin_argv;
239
240
if (sname != NULL)
241
sbuf_delete(sname);
242
return (error);
243
}
244
245
/*
246
* Tell kern_execve.c about it, with a little help from the linker.
247
*/
248
static struct execsw shell_execsw = {
249
.ex_imgact = exec_shell_imgact,
250
.ex_name = "#!"
251
};
252
EXEC_SET(shell, shell_execsw);
253
254