• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   *  linux/fs/binfmt_aout.c
4   *
5   *  Copyright (C) 1991, 1992, 1996  Linus Torvalds
6   */
7  
8  #include <linux/module.h>
9  
10  #include <linux/time.h>
11  #include <linux/kernel.h>
12  #include <linux/mm.h>
13  #include <linux/mman.h>
14  #include <linux/a.out.h>
15  #include <linux/errno.h>
16  #include <linux/signal.h>
17  #include <linux/string.h>
18  #include <linux/fs.h>
19  #include <linux/file.h>
20  #include <linux/stat.h>
21  #include <linux/fcntl.h>
22  #include <linux/ptrace.h>
23  #include <linux/user.h>
24  #include <linux/binfmts.h>
25  #include <linux/personality.h>
26  #include <linux/init.h>
27  #include <linux/coredump.h>
28  #include <linux/slab.h>
29  #include <linux/sched/task_stack.h>
30  
31  #include <linux/uaccess.h>
32  #include <asm/cacheflush.h>
33  
34  static int load_aout_binary(struct linux_binprm *);
35  static int load_aout_library(struct file*);
36  
37  static struct linux_binfmt aout_format = {
38  	.module		= THIS_MODULE,
39  	.load_binary	= load_aout_binary,
40  	.load_shlib	= load_aout_library,
41  };
42  
43  #define BAD_ADDR(x)	((unsigned long)(x) >= TASK_SIZE)
44  
set_brk(unsigned long start,unsigned long end)45  static int set_brk(unsigned long start, unsigned long end)
46  {
47  	start = PAGE_ALIGN(start);
48  	end = PAGE_ALIGN(end);
49  	if (end > start)
50  		return vm_brk(start, end - start);
51  	return 0;
52  }
53  
54  /*
55   * create_aout_tables() parses the env- and arg-strings in new user
56   * memory and creates the pointer tables from them, and puts their
57   * addresses on the "stack", returning the new stack pointer value.
58   */
create_aout_tables(char __user * p,struct linux_binprm * bprm)59  static unsigned long __user *create_aout_tables(char __user *p, struct linux_binprm * bprm)
60  {
61  	char __user * __user *argv;
62  	char __user * __user *envp;
63  	unsigned long __user *sp;
64  	int argc = bprm->argc;
65  	int envc = bprm->envc;
66  
67  	sp = (void __user *)((-(unsigned long)sizeof(char *)) & (unsigned long) p);
68  #ifdef __alpha__
69  /* whee.. test-programs are so much fun. */
70  	put_user(0, --sp);
71  	put_user(0, --sp);
72  	if (bprm->loader) {
73  		put_user(0, --sp);
74  		put_user(1003, --sp);
75  		put_user(bprm->loader, --sp);
76  		put_user(1002, --sp);
77  	}
78  	put_user(bprm->exec, --sp);
79  	put_user(1001, --sp);
80  #endif
81  	sp -= envc+1;
82  	envp = (char __user * __user *) sp;
83  	sp -= argc+1;
84  	argv = (char __user * __user *) sp;
85  #ifndef __alpha__
86  	put_user((unsigned long) envp,--sp);
87  	put_user((unsigned long) argv,--sp);
88  #endif
89  	put_user(argc,--sp);
90  	current->mm->arg_start = (unsigned long) p;
91  	while (argc-->0) {
92  		char c;
93  		put_user(p,argv++);
94  		do {
95  			get_user(c,p++);
96  		} while (c);
97  	}
98  	put_user(NULL,argv);
99  	current->mm->arg_end = current->mm->env_start = (unsigned long) p;
100  	while (envc-->0) {
101  		char c;
102  		put_user(p,envp++);
103  		do {
104  			get_user(c,p++);
105  		} while (c);
106  	}
107  	put_user(NULL,envp);
108  	current->mm->env_end = (unsigned long) p;
109  	return sp;
110  }
111  
112  /*
113   * These are the functions used to load a.out style executables and shared
114   * libraries.  There is no binary dependent code anywhere else.
115   */
116  
load_aout_binary(struct linux_binprm * bprm)117  static int load_aout_binary(struct linux_binprm * bprm)
118  {
119  	struct pt_regs *regs = current_pt_regs();
120  	struct exec ex;
121  	unsigned long error;
122  	unsigned long fd_offset;
123  	unsigned long rlim;
124  	int retval;
125  
126  	ex = *((struct exec *) bprm->buf);		/* exec-header */
127  	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
128  	     N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
129  	    N_TRSIZE(ex) || N_DRSIZE(ex) ||
130  	    i_size_read(file_inode(bprm->file)) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
131  		return -ENOEXEC;
132  	}
133  
134  	/*
135  	 * Requires a mmap handler. This prevents people from using a.out
136  	 * as part of an exploit attack against /proc-related vulnerabilities.
137  	 */
138  	if (!bprm->file->f_op->mmap)
139  		return -ENOEXEC;
140  
141  	fd_offset = N_TXTOFF(ex);
142  
143  	/* Check initial limits. This avoids letting people circumvent
144  	 * size limits imposed on them by creating programs with large
145  	 * arrays in the data or bss.
146  	 */
147  	rlim = rlimit(RLIMIT_DATA);
148  	if (rlim >= RLIM_INFINITY)
149  		rlim = ~0;
150  	if (ex.a_data + ex.a_bss > rlim)
151  		return -ENOMEM;
152  
153  	/* Flush all traces of the currently running executable */
154  	retval = begin_new_exec(bprm);
155  	if (retval)
156  		return retval;
157  
158  	/* OK, This is the point of no return */
159  #ifdef __alpha__
160  	SET_AOUT_PERSONALITY(bprm, ex);
161  #else
162  	set_personality(PER_LINUX);
163  #endif
164  	setup_new_exec(bprm);
165  
166  	current->mm->end_code = ex.a_text +
167  		(current->mm->start_code = N_TXTADDR(ex));
168  	current->mm->end_data = ex.a_data +
169  		(current->mm->start_data = N_DATADDR(ex));
170  	current->mm->brk = ex.a_bss +
171  		(current->mm->start_brk = N_BSSADDR(ex));
172  
173  	retval = setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT);
174  	if (retval < 0)
175  		return retval;
176  
177  
178  	if (N_MAGIC(ex) == OMAGIC) {
179  		unsigned long text_addr, map_size;
180  		loff_t pos;
181  
182  		text_addr = N_TXTADDR(ex);
183  
184  #ifdef __alpha__
185  		pos = fd_offset;
186  		map_size = ex.a_text+ex.a_data + PAGE_SIZE - 1;
187  #else
188  		pos = 32;
189  		map_size = ex.a_text+ex.a_data;
190  #endif
191  		error = vm_brk(text_addr & PAGE_MASK, map_size);
192  		if (error)
193  			return error;
194  
195  		error = read_code(bprm->file, text_addr, pos,
196  				  ex.a_text+ex.a_data);
197  		if ((signed long)error < 0)
198  			return error;
199  	} else {
200  		if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
201  		    (N_MAGIC(ex) != NMAGIC) && printk_ratelimit())
202  		{
203  			printk(KERN_NOTICE "executable not page aligned\n");
204  		}
205  
206  		if ((fd_offset & ~PAGE_MASK) != 0 && printk_ratelimit())
207  		{
208  			printk(KERN_WARNING
209  			       "fd_offset is not page aligned. Please convert program: %pD\n",
210  			       bprm->file);
211  		}
212  
213  		if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
214  			error = vm_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
215  			if (error)
216  				return error;
217  
218  			read_code(bprm->file, N_TXTADDR(ex), fd_offset,
219  				  ex.a_text + ex.a_data);
220  			goto beyond_if;
221  		}
222  
223  		error = vm_mmap(bprm->file, N_TXTADDR(ex), ex.a_text,
224  			PROT_READ | PROT_EXEC,
225  			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
226  			fd_offset);
227  
228  		if (error != N_TXTADDR(ex))
229  			return error;
230  
231  		error = vm_mmap(bprm->file, N_DATADDR(ex), ex.a_data,
232  				PROT_READ | PROT_WRITE | PROT_EXEC,
233  				MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
234  				fd_offset + ex.a_text);
235  		if (error != N_DATADDR(ex))
236  			return error;
237  	}
238  beyond_if:
239  	set_binfmt(&aout_format);
240  
241  	retval = set_brk(current->mm->start_brk, current->mm->brk);
242  	if (retval < 0)
243  		return retval;
244  
245  	current->mm->start_stack =
246  		(unsigned long) create_aout_tables((char __user *) bprm->p, bprm);
247  #ifdef __alpha__
248  	regs->gp = ex.a_gpvalue;
249  #endif
250  	finalize_exec(bprm);
251  	start_thread(regs, ex.a_entry, current->mm->start_stack);
252  	return 0;
253  }
254  
load_aout_library(struct file * file)255  static int load_aout_library(struct file *file)
256  {
257  	struct inode * inode;
258  	unsigned long bss, start_addr, len;
259  	unsigned long error;
260  	int retval;
261  	struct exec ex;
262  	loff_t pos = 0;
263  
264  	inode = file_inode(file);
265  
266  	retval = -ENOEXEC;
267  	error = kernel_read(file, &ex, sizeof(ex), &pos);
268  	if (error != sizeof(ex))
269  		goto out;
270  
271  	/* We come in here for the regular a.out style of shared libraries */
272  	if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
273  	    N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
274  	    i_size_read(inode) < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
275  		goto out;
276  	}
277  
278  	/*
279  	 * Requires a mmap handler. This prevents people from using a.out
280  	 * as part of an exploit attack against /proc-related vulnerabilities.
281  	 */
282  	if (!file->f_op->mmap)
283  		goto out;
284  
285  	if (N_FLAGS(ex))
286  		goto out;
287  
288  	/* For  QMAGIC, the starting address is 0x20 into the page.  We mask
289  	   this off to get the starting address for the page */
290  
291  	start_addr =  ex.a_entry & 0xfffff000;
292  
293  	if ((N_TXTOFF(ex) & ~PAGE_MASK) != 0) {
294  		if (printk_ratelimit())
295  		{
296  			printk(KERN_WARNING
297  			       "N_TXTOFF is not page aligned. Please convert library: %pD\n",
298  			       file);
299  		}
300  		retval = vm_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
301  		if (retval)
302  			goto out;
303  
304  		read_code(file, start_addr, N_TXTOFF(ex),
305  			  ex.a_text + ex.a_data);
306  		retval = 0;
307  		goto out;
308  	}
309  	/* Now use mmap to map the library into memory. */
310  	error = vm_mmap(file, start_addr, ex.a_text + ex.a_data,
311  			PROT_READ | PROT_WRITE | PROT_EXEC,
312  			MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
313  			N_TXTOFF(ex));
314  	retval = error;
315  	if (error != start_addr)
316  		goto out;
317  
318  	len = PAGE_ALIGN(ex.a_text + ex.a_data);
319  	bss = ex.a_text + ex.a_data + ex.a_bss;
320  	if (bss > len) {
321  		retval = vm_brk(start_addr + len, bss - len);
322  		if (retval)
323  			goto out;
324  	}
325  	retval = 0;
326  out:
327  	return retval;
328  }
329  
init_aout_binfmt(void)330  static int __init init_aout_binfmt(void)
331  {
332  	register_binfmt(&aout_format);
333  	return 0;
334  }
335  
exit_aout_binfmt(void)336  static void __exit exit_aout_binfmt(void)
337  {
338  	unregister_binfmt(&aout_format);
339  }
340  
341  core_initcall(init_aout_binfmt);
342  module_exit(exit_aout_binfmt);
343  MODULE_LICENSE("GPL");
344