diff options
Diffstat (limited to 'src/fs/exec.c')
-rw-r--r-- | src/fs/exec.c | 353 |
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 | |||
31 | extern int sys_exit(int exit_code); | ||
32 | extern 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 | */ | ||
46 | static 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 | */ | ||
75 | static 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 | */ | ||
104 | static 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 | |||
154 | static 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 | */ | ||
182 | int 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 | |||
204 | restart_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; | ||
347 | exec_error2: | ||
348 | iput(inode); | ||
349 | exec_error1: | ||
350 | for (i=0 ; i<MAX_ARG_PAGES ; i++) | ||
351 | free_page(page[i]); | ||
352 | return(retval); | ||
353 | } | ||