linux_emul.c revision 302962
1/*-
2 * Copyright (c) 2006 Roman Divacky
3 * Copyright (c) 2013 Dmitry Chagin
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer
11 *    in this position and unchanged.
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 * 3. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/compat/linux/linux_emul.c 302962 2016-07-17 15:07:33Z dchagin $");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/imgact.h>
36#include <sys/kernel.h>
37#include <sys/ktr.h>
38#include <sys/lock.h>
39#include <sys/malloc.h>
40#include <sys/mutex.h>
41#include <sys/sx.h>
42#include <sys/proc.h>
43#include <sys/syscallsubr.h>
44#include <sys/sysent.h>
45
46#include <compat/linux/linux_emul.h>
47#include <compat/linux/linux_misc.h>
48#include <compat/linux/linux_persona.h>
49#include <compat/linux/linux_util.h>
50
51
52/*
53 * This returns reference to the thread emuldata entry (if found)
54 *
55 * Hold PROC_LOCK when referencing emuldata from other threads.
56 */
57struct linux_emuldata *
58em_find(struct thread *td)
59{
60	struct linux_emuldata *em;
61
62	em = td->td_emuldata;
63
64	return (em);
65}
66
67/*
68 * This returns reference to the proc pemuldata entry (if found)
69 *
70 * Hold PROC_LOCK when referencing proc pemuldata from other threads.
71 * Hold LINUX_PEM_LOCK wher referencing pemuldata members.
72 */
73struct linux_pemuldata *
74pem_find(struct proc *p)
75{
76	struct linux_pemuldata *pem;
77
78	pem = p->p_emuldata;
79
80	return (pem);
81}
82
83void
84linux_proc_init(struct thread *td, struct thread *newtd, int flags)
85{
86	struct linux_emuldata *em;
87	struct linux_pemuldata *pem;
88	struct epoll_emuldata *emd;
89	struct proc *p;
90
91	if (newtd != NULL) {
92		p = newtd->td_proc;
93
94		/* non-exec call */
95		em = malloc(sizeof(*em), M_TEMP, M_WAITOK | M_ZERO);
96		if (flags & LINUX_CLONE_THREAD) {
97			LINUX_CTR1(proc_init, "thread newtd(%d)",
98			    newtd->td_tid);
99
100			em->em_tid = newtd->td_tid;
101		} else {
102			LINUX_CTR1(proc_init, "fork newtd(%d)", p->p_pid);
103
104			em->em_tid = p->p_pid;
105
106			pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO);
107			sx_init(&pem->pem_sx, "lpemlk");
108			p->p_emuldata = pem;
109		}
110		newtd->td_emuldata = em;
111	} else {
112		p = td->td_proc;
113
114		/* exec */
115		LINUX_CTR1(proc_init, "exec newtd(%d)", p->p_pid);
116
117		/* lookup the old one */
118		em = em_find(td);
119		KASSERT(em != NULL, ("proc_init: emuldata not found in exec case.\n"));
120
121		em->em_tid = p->p_pid;
122		em->flags = 0;
123		em->pdeath_signal = 0;
124		em->robust_futexes = NULL;
125		em->child_clear_tid = NULL;
126		em->child_set_tid = NULL;
127
128		 /* epoll should be destroyed in a case of exec. */
129		pem = pem_find(p);
130		KASSERT(pem != NULL, ("proc_exit: proc emuldata not found.\n"));
131		pem->persona = 0;
132		if (pem->epoll != NULL) {
133			emd = pem->epoll;
134			pem->epoll = NULL;
135			free(emd, M_EPOLL);
136		}
137	}
138
139}
140
141void
142linux_proc_exit(void *arg __unused, struct proc *p)
143{
144	struct linux_pemuldata *pem;
145	struct epoll_emuldata *emd;
146	struct thread *td = curthread;
147
148	if (__predict_false(SV_CURPROC_ABI() != SV_ABI_LINUX))
149		return;
150
151	LINUX_CTR3(proc_exit, "thread(%d) proc(%d) p %p",
152	    td->td_tid, p->p_pid, p);
153
154	pem = pem_find(p);
155	if (pem == NULL)
156		return;
157	(p->p_sysent->sv_thread_detach)(td);
158
159	p->p_emuldata = NULL;
160
161	if (pem->epoll != NULL) {
162		emd = pem->epoll;
163		pem->epoll = NULL;
164		free(emd, M_EPOLL);
165	}
166
167	sx_destroy(&pem->pem_sx);
168	free(pem, M_LINUX);
169}
170
171int
172linux_common_execve(struct thread *td, struct image_args *eargs)
173{
174	struct linux_pemuldata *pem;
175	struct epoll_emuldata *emd;
176	struct vmspace *oldvmspace;
177	struct linux_emuldata *em;
178	struct proc *p;
179	int error;
180
181	p = td->td_proc;
182
183	error = pre_execve(td, &oldvmspace);
184	if (error != 0)
185		return (error);
186
187	error = kern_execve(td, eargs, NULL);
188	post_execve(td, error, oldvmspace);
189	if (error != 0)
190		return (error);
191
192	/*
193	 * In a case of transition from Linux binary execing to
194	 * FreeBSD binary we destroy linux emuldata thread & proc entries.
195	 */
196	if (SV_CURPROC_ABI() != SV_ABI_LINUX) {
197		PROC_LOCK(p);
198		em = em_find(td);
199		KASSERT(em != NULL, ("proc_exec: thread emuldata not found.\n"));
200		td->td_emuldata = NULL;
201
202		pem = pem_find(p);
203		KASSERT(pem != NULL, ("proc_exec: proc pemuldata not found.\n"));
204		p->p_emuldata = NULL;
205		PROC_UNLOCK(p);
206
207		if (pem->epoll != NULL) {
208			emd = pem->epoll;
209			pem->epoll = NULL;
210			free(emd, M_EPOLL);
211		}
212
213		free(em, M_TEMP);
214		free(pem, M_LINUX);
215	}
216	return (0);
217}
218
219void
220linux_proc_exec(void *arg __unused, struct proc *p, struct image_params *imgp)
221{
222	struct thread *td = curthread;
223	struct thread *othertd;
224#if defined(__amd64__)
225	struct linux_pemuldata *pem;
226#endif
227
228	/*
229	 * In a case of execing from linux binary properly detach
230	 * other threads from the user space.
231	 */
232	if (__predict_false(SV_PROC_ABI(p) == SV_ABI_LINUX)) {
233		FOREACH_THREAD_IN_PROC(p, othertd) {
234			if (td != othertd)
235				(p->p_sysent->sv_thread_detach)(othertd);
236		}
237	}
238
239	/*
240	 * In a case of execing to linux binary we create linux
241	 * emuldata thread entry.
242	 */
243	if (__predict_false((imgp->sysent->sv_flags & SV_ABI_MASK) ==
244	    SV_ABI_LINUX)) {
245
246		if (SV_PROC_ABI(p) == SV_ABI_LINUX)
247			linux_proc_init(td, NULL, 0);
248		else
249			linux_proc_init(td, td, 0);
250#if defined(__amd64__)
251		/*
252		 * An IA32 executable which has executable stack will have the
253		 * READ_IMPLIES_EXEC personality flag set automatically.
254		 */
255		if (SV_PROC_FLAG(td->td_proc, SV_ILP32) &&
256		    imgp->stack_prot & VM_PROT_EXECUTE) {
257			pem = pem_find(p);
258			pem->persona |= LINUX_READ_IMPLIES_EXEC;
259		}
260#endif
261	}
262}
263
264void
265linux_thread_dtor(void *arg __unused, struct thread *td)
266{
267	struct linux_emuldata *em;
268
269	em = em_find(td);
270	if (em == NULL)
271		return;
272	td->td_emuldata = NULL;
273
274	LINUX_CTR1(thread_dtor, "thread(%d)", em->em_tid);
275
276	free(em, M_TEMP);
277}
278
279void
280linux_schedtail(struct thread *td)
281{
282	struct linux_emuldata *em;
283	struct proc *p;
284	int error = 0;
285	int *child_set_tid;
286
287	p = td->td_proc;
288
289	em = em_find(td);
290	KASSERT(em != NULL, ("linux_schedtail: thread emuldata not found.\n"));
291	child_set_tid = em->child_set_tid;
292
293	if (child_set_tid != NULL) {
294		error = copyout(&em->em_tid, child_set_tid,
295		    sizeof(em->em_tid));
296		LINUX_CTR4(schedtail, "thread(%d) %p stored %d error %d",
297		    td->td_tid, child_set_tid, em->em_tid, error);
298	} else
299		LINUX_CTR1(schedtail, "thread(%d)", em->em_tid);
300}
301