summaryrefslogtreecommitdiffstats
path: root/src/fs/exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/exec.c')
-rw-r--r--src/fs/exec.c353
1 files changed, 353 insertions, 0 deletions
diff --git a/src/fs/exec.c b/src/fs/exec.c
new file mode 100644
index 0000000..d7efa43
--- /dev/null
+++ b/src/fs/exec.c
@@ -0,0 +1,353 @@
1/*
2 * linux/fs/exec.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
6
7/*
8 * #!-checking implemented by tytso.
9 */
10
11/*
12 * Demand-loading implemented 01.12.91 - no need to read anything but
13 * the header into memory. The inode of the executable is put into
14 * "current->executable", and page faults do the actual loading. Clean.
15 *
16 * Once more I can proudly say that linux stood up to being changed: it
17 * was less than 2 hours work to get demand-loading completely implemented.
18 */
19
20#include <errno.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <a.out.h>
24
25#include <linux/fs.h>
26#include <linux/sched.h>
27#include <linux/kernel.h>
28#include <linux/mm.h>
29#include <asm/segment.h>
30
31extern int sys_exit(int exit_code);
32extern int sys_close(int fd);
33
34/*
35 * MAX_ARG_PAGES defines the number of pages allocated for arguments
36 * and envelope for the new program. 32 should suffice, this gives
37 * a maximum env+arg of 128kB !
38 */
39#define MAX_ARG_PAGES 32
40
41/*
42 * create_tables() parses the env- and arg-strings in new user
43 * memory and creates the pointer tables from them, and puts their
44 * addresses on the "stack", returning the new stack pointer value.
45 */
46static unsigned long * create_tables(char * p,int argc,int envc)
47{
48 unsigned long *argv,*envp;
49 unsigned long * sp;
50
51 sp = (unsigned long *) (0xfffffffc & (unsigned long) p);
52 sp -= envc+1;
53 envp = sp;
54 sp -= argc+1;
55 argv = sp;
56 put_fs_long((unsigned long)envp,--sp);
57 put_fs_long((unsigned long)argv,--sp);
58 put_fs_long((unsigned long)argc,--sp);
59 while (argc-->0) {
60 put_fs_long((unsigned long) p,argv++);
61 while (get_fs_byte(p++)) /* nothing */ ;
62 }
63 put_fs_long(0,argv);
64 while (envc-->0) {
65 put_fs_long((unsigned long) p,envp++);
66 while (get_fs_byte(p++)) /* nothing */ ;
67 }
68 put_fs_long(0,envp);
69 return sp;
70}
71
72/*
73 * count() counts the number of arguments/envelopes
74 */
75static int count(char ** argv)
76{
77 int i=0;
78 char ** tmp;
79
80 if ((tmp = argv))
81 while (get_fs_long((unsigned long *) (tmp++)))
82 i++;
83
84 return i;
85}
86
87/*
88 * 'copy_string()' copies argument/envelope strings from user
89 * memory to free pages in kernel mem. These are in a format ready
90 * to be put directly into the top of new user memory.
91 *
92 * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
93 * whether the string and the string array are from user or kernel segments:
94 *
95 * from_kmem argv * argv **
96 * 0 user space user space
97 * 1 kernel space user space
98 * 2 kernel space kernel space
99 *
100 * We do this by playing games with the fs segment register. Since it
101 * it is expensive to load a segment register, we try to avoid calling
102 * set_fs() unless we absolutely have to.
103 */
104static unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
105 unsigned long p, int from_kmem)
106{
107 char *tmp, *pag=NULL;
108 int len, offset = 0;
109 unsigned long old_fs, new_fs;
110
111 if (!p)
112 return 0; /* bullet-proofing */
113 new_fs = get_ds();
114 old_fs = get_fs();
115 if (from_kmem==2)
116 set_fs(new_fs);
117 while (argc-- > 0) {
118 if (from_kmem == 1)
119 set_fs(new_fs);
120 if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc)))
121 panic("argc is wrong");
122 if (from_kmem == 1)
123 set_fs(old_fs);
124 len=0; /* remember zero-padding */
125 do {
126 len++;
127 } while (get_fs_byte(tmp++));
128 if (p-len < 0) { /* this shouldn't happen - 128kB */
129 set_fs(old_fs);
130 return 0;
131 }
132 while (len) {
133 --p; --tmp; --len;
134 if (--offset < 0) {
135 offset = p % PAGE_SIZE;
136 if (from_kmem==2)
137 set_fs(old_fs);
138 if (!(pag = (char *) page[p/PAGE_SIZE]) &&
139 !(pag = (char *) (page[p/PAGE_SIZE] =
140 get_free_page())))
141 return 0;
142 if (from_kmem==2)
143 set_fs(new_fs);
144
145 }
146 *(pag + offset) = get_fs_byte(tmp);
147 }
148 }
149 if (from_kmem==2)
150 set_fs(old_fs);
151 return p;
152}
153
154static unsigned long change_ldt(unsigned long text_size,unsigned long * page)
155{
156 unsigned long code_limit,data_limit,code_base,data_base;
157 int i;
158
159 code_limit = text_size+PAGE_SIZE -1;
160 code_limit &= 0xFFFFF000;
161 data_limit = 0x4000000;
162 code_base = get_base(current->ldt[1]);
163 data_base = code_base;
164 set_base(current->ldt[1],code_base);
165 set_limit(current->ldt[1],code_limit);
166 set_base(current->ldt[2],data_base);
167 set_limit(current->ldt[2],data_limit);
168/* make sure fs points to the NEW data segment */
169 __asm__("pushl $0x17\n\tpop %%fs"::);
170 data_base += data_limit;
171 for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) {
172 data_base -= PAGE_SIZE;
173 if (page[i])
174 put_page(page[i],data_base);
175 }
176 return data_limit;
177}
178
179/*
180 * 'do_execve()' executes a new program.
181 */
182int do_execve(unsigned long * eip,long tmp,char * filename,
183 char ** argv, char ** envp)
184{
185 struct m_inode * inode;
186 struct buffer_head * bh;
187 struct exec ex;
188 unsigned long page[MAX_ARG_PAGES];
189 int i,argc,envc;
190 int e_uid, e_gid;
191 int retval;
192 int sh_bang = 0;
193 unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4;
194
195 if ((0xffff & eip[1]) != 0x000f)
196 panic("execve called from supervisor mode");
197 for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
198 page[i]=0;
199 if (!(inode=namei(filename))) /* get executables inode */
200 return -ENOENT;
201 argc = count(argv);
202 envc = count(envp);
203
204restart_interp:
205 if (!S_ISREG(inode->i_mode)) { /* must be regular file */
206 retval = -EACCES;
207 goto exec_error2;
208 }
209 i = inode->i_mode;
210 e_uid = (i & S_ISUID) ? inode->i_uid : current->euid;
211 e_gid = (i & S_ISGID) ? inode->i_gid : current->egid;
212 if (current->euid == inode->i_uid)
213 i >>= 6;
214 else if (current->egid == inode->i_gid)
215 i >>= 3;
216 if (!(i & 1) &&
217 !((inode->i_mode & 0111) && suser())) {
218 retval = -ENOEXEC;
219 goto exec_error2;
220 }
221 if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) {
222 retval = -EACCES;
223 goto exec_error2;
224 }
225 ex = *((struct exec *) bh->b_data); /* read exec-header */
226 if ((bh->b_data[0] == '#') && (bh->b_data[1] == '!') && (!sh_bang)) {
227 /*
228 * This section does the #! interpretation.
229 * Sorta complicated, but hopefully it will work. -TYT
230 */
231
232 char buf[1023], *cp, *interp, *i_name, *i_arg;
233 unsigned long old_fs;
234
235 strncpy(buf, bh->b_data+2, 1022);
236 brelse(bh);
237 iput(inode);
238 buf[1022] = '\0';
239 if ((cp = strchr(buf, '\n'))) {
240 *cp = '\0';
241 for (cp = buf; (*cp == ' ') || (*cp == '\t'); cp++);
242 }
243 if (!cp || *cp == '\0') {
244 retval = -ENOEXEC; /* No interpreter name found */
245 goto exec_error1;
246 }
247 interp = i_name = cp;
248 i_arg = 0;
249 for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
250 if (*cp == '/')
251 i_name = cp+1;
252 }
253 if (*cp) {
254 *cp++ = '\0';
255 i_arg = cp;
256 }
257 /*
258 * OK, we've parsed out the interpreter name and
259 * (optional) argument.
260 */
261 if (sh_bang++ == 0) {
262 p = copy_strings(envc, envp, page, p, 0);
263 p = copy_strings(--argc, argv+1, page, p, 0);
264 }
265 /*
266 * Splice in (1) the interpreter's name for argv[0]
267 * (2) (optional) argument to interpreter
268 * (3) filename of shell script
269 *
270 * This is done in reverse order, because of how the
271 * user environment and arguments are stored.
272 */
273 p = copy_strings(1, &filename, page, p, 1);
274 argc++;
275 if (i_arg) {
276 p = copy_strings(1, &i_arg, page, p, 2);
277 argc++;
278 }
279 p = copy_strings(1, &i_name, page, p, 2);
280 argc++;
281 if (!p) {
282 retval = -ENOMEM;
283 goto exec_error1;
284 }
285 /*
286 * OK, now restart the process with the interpreter's inode.
287 */
288 old_fs = get_fs();
289 set_fs(get_ds());
290 if (!(inode=namei(interp))) { /* get executables inode */
291 set_fs(old_fs);
292 retval = -ENOENT;
293 goto exec_error1;
294 }
295 set_fs(old_fs);
296 goto restart_interp;
297 }
298 brelse(bh);
299 if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize ||
300 ex.a_text+ex.a_data+ex.a_bss>0x3000000 ||
301 inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) {
302 retval = -ENOEXEC;
303 goto exec_error2;
304 }
305 if (N_TXTOFF(ex) != BLOCK_SIZE) {
306 printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename);
307 retval = -ENOEXEC;
308 goto exec_error2;
309 }
310 if (!sh_bang) {
311 p = copy_strings(envc,envp,page,p,0);
312 p = copy_strings(argc,argv,page,p,0);
313 if (!p) {
314 retval = -ENOMEM;
315 goto exec_error2;
316 }
317 }
318/* OK, This is the point of no return */
319 if (current->executable)
320 iput(current->executable);
321 current->executable = inode;
322 for (i=0 ; i<32 ; i++)
323 current->sigaction[i].sa_handler = NULL;
324 for (i=0 ; i<NR_OPEN ; i++)
325 if ((current->close_on_exec>>i)&1)
326 sys_close(i);
327 current->close_on_exec = 0;
328 free_page_tables(get_base(current->ldt[1]),get_limit(0x0f));
329 free_page_tables(get_base(current->ldt[2]),get_limit(0x17));
330 if (last_task_used_math == current)
331 last_task_used_math = NULL;
332 current->used_math = 0;
333 p += change_ldt(ex.a_text,page)-MAX_ARG_PAGES*PAGE_SIZE;
334 p = (unsigned long) create_tables((char *)p,argc,envc);
335 current->brk = ex.a_bss +
336 (current->end_data = ex.a_data +
337 (current->end_code = ex.a_text));
338 current->start_stack = p & 0xfffff000;
339 current->euid = e_uid;
340 current->egid = e_gid;
341 i = ex.a_text+ex.a_data;
342 while (i&0xfff)
343 put_fs_byte(0,(char *) (i++));
344 eip[0] = ex.a_entry; /* eip, magic happens :-) */
345 eip[3] = p; /* stack pointer */
346 return 0;
347exec_error2:
348 iput(inode);
349exec_error1:
350 for (i=0 ; i<MAX_ARG_PAGES ; i++)
351 free_page(page[i]);
352 return(retval);
353}