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