xref: /freebsd/sys/compat/linux/linux_ptrace.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2017 Edward Tomasz Napierala <trasz@FreeBSD.org>
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
8  * ("CTSRD"), as part of the DARPA CRASH research programme.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/lock.h>
35 #include <sys/proc.h>
36 #include <sys/ptrace.h>
37 #include <sys/sx.h>
38 #include <sys/syscallsubr.h>
39 
40 #include <machine/../linux/linux.h>
41 #include <machine/../linux/linux_proto.h>
42 #include <compat/linux/linux_emul.h>
43 #include <compat/linux/linux_errno.h>
44 #include <compat/linux/linux_misc.h>
45 #include <compat/linux/linux_signal.h>
46 #include <compat/linux/linux_util.h>
47 
48 #define	LINUX_PTRACE_TRACEME		0
49 #define	LINUX_PTRACE_PEEKTEXT		1
50 #define	LINUX_PTRACE_PEEKDATA		2
51 #define	LINUX_PTRACE_PEEKUSER		3
52 #define	LINUX_PTRACE_POKETEXT		4
53 #define	LINUX_PTRACE_POKEDATA		5
54 #define	LINUX_PTRACE_POKEUSER		6
55 #define	LINUX_PTRACE_CONT		7
56 #define	LINUX_PTRACE_KILL		8
57 #define	LINUX_PTRACE_SINGLESTEP		9
58 #define	LINUX_PTRACE_GETREGS		12
59 #define	LINUX_PTRACE_SETREGS		13
60 #define	LINUX_PTRACE_GETFPREGS		14
61 #define	LINUX_PTRACE_SETFPREGS		15
62 #define	LINUX_PTRACE_ATTACH		16
63 #define	LINUX_PTRACE_DETACH		17
64 #define	LINUX_PTRACE_SYSCALL		24
65 #define	LINUX_PTRACE_SETOPTIONS		0x4200
66 #define	LINUX_PTRACE_GETEVENTMSG	0x4201
67 #define	LINUX_PTRACE_GETSIGINFO		0x4202
68 #define	LINUX_PTRACE_GETREGSET		0x4204
69 #define	LINUX_PTRACE_SEIZE		0x4206
70 #define	LINUX_PTRACE_GET_SYSCALL_INFO	0x420e
71 
72 #define	LINUX_PTRACE_EVENT_EXEC		4
73 #define	LINUX_PTRACE_EVENT_EXIT		6
74 
75 #define	LINUX_PTRACE_O_TRACESYSGOOD	1
76 #define	LINUX_PTRACE_O_TRACEFORK	2
77 #define	LINUX_PTRACE_O_TRACEVFORK	4
78 #define	LINUX_PTRACE_O_TRACECLONE	8
79 #define	LINUX_PTRACE_O_TRACEEXEC	16
80 #define	LINUX_PTRACE_O_TRACEVFORKDONE	32
81 #define	LINUX_PTRACE_O_TRACEEXIT	64
82 #define	LINUX_PTRACE_O_TRACESECCOMP	128
83 #define	LINUX_PTRACE_O_EXITKILL		1048576
84 #define	LINUX_PTRACE_O_SUSPEND_SECCOMP	2097152
85 
86 #define	LINUX_NT_PRSTATUS		0x1
87 #define	LINUX_NT_PRFPREG		0x2
88 #define	LINUX_NT_X86_XSTATE		0x202
89 
90 #define	LINUX_PTRACE_O_MASK	(LINUX_PTRACE_O_TRACESYSGOOD |	\
91     LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK |	\
92     LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC |	\
93     LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT |	\
94     LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL |	\
95     LINUX_PTRACE_O_SUSPEND_SECCOMP)
96 
97 #define	LINUX_PTRACE_SYSCALL_INFO_NONE	0
98 #define	LINUX_PTRACE_SYSCALL_INFO_ENTRY	1
99 #define	LINUX_PTRACE_SYSCALL_INFO_EXIT	2
100 
101 static int
102 map_signum(int lsig, int *bsigp)
103 {
104 	int bsig;
105 
106 	if (lsig == 0) {
107 		*bsigp = 0;
108 		return (0);
109 	}
110 
111 	if (lsig < 0 || lsig > LINUX_SIGRTMAX)
112 		return (EINVAL);
113 
114 	bsig = linux_to_bsd_signal(lsig);
115 	if (bsig == SIGSTOP)
116 		bsig = 0;
117 
118 	*bsigp = bsig;
119 	return (0);
120 }
121 
122 int
123 linux_ptrace_status(struct thread *td, pid_t pid, int status)
124 {
125 	struct ptrace_lwpinfo lwpinfo;
126 	struct linux_pemuldata *pem;
127 	register_t saved_retval;
128 	int error;
129 
130 	saved_retval = td->td_retval[0];
131 	error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
132 	td->td_retval[0] = saved_retval;
133 	if (error != 0) {
134 		linux_msg(td, "PT_LWPINFO failed with error %d", error);
135 		return (status);
136 	}
137 
138 	pem = pem_find(td->td_proc);
139 	KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__));
140 
141 	LINUX_PEM_SLOCK(pem);
142 	if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) &&
143 	    lwpinfo.pl_flags & PL_FLAG_SCE)
144 		status |= (LINUX_SIGTRAP | 0x80) << 8;
145 	if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) &&
146 	    lwpinfo.pl_flags & PL_FLAG_SCX) {
147 		if (lwpinfo.pl_flags & PL_FLAG_EXEC)
148 			status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXEC << 8) << 8;
149 		else
150 			status |= (LINUX_SIGTRAP | 0x80) << 8;
151 	}
152 	if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACEEXIT) &&
153 	    lwpinfo.pl_flags & PL_FLAG_EXITED)
154 		status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXIT << 8) << 8;
155 	LINUX_PEM_SUNLOCK(pem);
156 
157 	return (status);
158 }
159 
160 static int
161 linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data)
162 {
163 	int error;
164 
165 	error = kern_ptrace(td, PT_READ_I, pid, addr, 0);
166 	if (error == 0)
167 		error = copyout(td->td_retval, data, sizeof(l_int));
168 	else if (error == ENOMEM)
169 		error = EIO;
170 	td->td_retval[0] = error;
171 
172 	return (error);
173 }
174 
175 static int
176 linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data)
177 {
178 	struct linux_pemuldata *pem;
179 	int mask;
180 
181 	mask = 0;
182 
183 	if (data & ~LINUX_PTRACE_O_MASK) {
184 		linux_msg(td, "unknown ptrace option %lx set; "
185 		    "returning EINVAL",
186 		    data & ~LINUX_PTRACE_O_MASK);
187 		return (EINVAL);
188 	}
189 
190 	pem = pem_find(td->td_proc);
191 	KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__));
192 
193 	/*
194 	 * PTRACE_O_EXITKILL is ignored, we do that by default.
195 	 */
196 
197 	LINUX_PEM_XLOCK(pem);
198 	if (data & LINUX_PTRACE_O_TRACESYSGOOD) {
199 		pem->ptrace_flags |= LINUX_PTRACE_O_TRACESYSGOOD;
200 	} else {
201 		pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACESYSGOOD;
202 	}
203 	LINUX_PEM_XUNLOCK(pem);
204 
205 	if (data & LINUX_PTRACE_O_TRACEFORK)
206 		mask |= PTRACE_FORK;
207 
208 	if (data & LINUX_PTRACE_O_TRACEVFORK)
209 		mask |= PTRACE_VFORK;
210 
211 	if (data & LINUX_PTRACE_O_TRACECLONE)
212 		mask |= PTRACE_VFORK;
213 
214 	if (data & LINUX_PTRACE_O_TRACEEXEC)
215 		mask |= PTRACE_EXEC;
216 
217 	if (data & LINUX_PTRACE_O_TRACEVFORKDONE)
218 		mask |= PTRACE_VFORK; /* XXX: Close enough? */
219 
220 	if (data & LINUX_PTRACE_O_TRACEEXIT) {
221 		pem->ptrace_flags |= LINUX_PTRACE_O_TRACEEXIT;
222 	} else {
223 		pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACEEXIT;
224 	}
225 
226 	return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask)));
227 }
228 
229 static int
230 linux_ptrace_geteventmsg(struct thread *td, pid_t pid, l_ulong data)
231 {
232 
233 	linux_msg(td, "PTRACE_GETEVENTMSG not implemented; returning EINVAL");
234 	return (EINVAL);
235 }
236 
237 static int
238 linux_ptrace_getsiginfo(struct thread *td, pid_t pid, l_ulong data)
239 {
240 	struct ptrace_lwpinfo lwpinfo;
241 	l_siginfo_t l_siginfo;
242 	int error, sig;
243 
244 	error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
245 	if (error != 0) {
246 		linux_msg(td, "PT_LWPINFO failed with error %d", error);
247 		return (error);
248 	}
249 
250 	if ((lwpinfo.pl_flags & PL_FLAG_SI) == 0) {
251 		error = EINVAL;
252 		linux_msg(td, "no PL_FLAG_SI, returning %d", error);
253 		return (error);
254 	}
255 
256 	sig = bsd_to_linux_signal(lwpinfo.pl_siginfo.si_signo);
257 	memset(&l_siginfo, 0, sizeof(l_siginfo));
258 	siginfo_to_lsiginfo(&lwpinfo.pl_siginfo, &l_siginfo, sig);
259 	error = copyout(&l_siginfo, (void *)data, sizeof(l_siginfo));
260 	return (error);
261 }
262 
263 static int
264 linux_ptrace_getregs(struct thread *td, pid_t pid, void *data)
265 {
266 	struct reg b_reg;
267 	struct linux_pt_regset l_regset;
268 	int error;
269 
270 	error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
271 	if (error != 0)
272 		return (error);
273 
274 	bsd_to_linux_regset(&b_reg, &l_regset);
275 	error = linux_ptrace_getregs_machdep(td, pid, &l_regset);
276 	if (error != 0)
277 		return (error);
278 
279 	error = copyout(&l_regset, (void *)data, sizeof(l_regset));
280 	return (error);
281 }
282 
283 static int
284 linux_ptrace_setregs(struct thread *td, pid_t pid, void *data)
285 {
286 	struct reg b_reg;
287 	struct linux_pt_regset l_regset;
288 	int error;
289 
290 	error = copyin(data, &l_regset, sizeof(l_regset));
291 	if (error != 0)
292 		return (error);
293 	linux_to_bsd_regset(&b_reg, &l_regset);
294 	error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0);
295 	return (error);
296 }
297 
298 static int
299 linux_ptrace_getregset_prstatus(struct thread *td, pid_t pid, l_ulong data)
300 {
301 	struct reg b_reg;
302 	struct linux_pt_regset l_regset;
303 	struct iovec iov;
304 	size_t len;
305 	int error;
306 
307 	error = copyin((const void *)data, &iov, sizeof(iov));
308 	if (error != 0) {
309 		linux_msg(td, "copyin error %d", error);
310 		return (error);
311 	}
312 
313 	error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
314 	if (error != 0)
315 		return (error);
316 
317 	bsd_to_linux_regset(&b_reg, &l_regset);
318 	error = linux_ptrace_getregs_machdep(td, pid, &l_regset);
319 	if (error != 0)
320 		return (error);
321 
322 	len = MIN(iov.iov_len, sizeof(l_regset));
323 	error = copyout(&l_regset, (void *)iov.iov_base, len);
324 	if (error != 0) {
325 		linux_msg(td, "copyout error %d", error);
326 		return (error);
327 	}
328 
329 	iov.iov_len = len;
330 	error = copyout(&iov, (void *)data, sizeof(iov));
331 	if (error != 0) {
332 		linux_msg(td, "iov copyout error %d", error);
333 		return (error);
334 	}
335 
336 	return (error);
337 }
338 
339 static int
340 linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
341 {
342 
343 	switch (addr) {
344 	case LINUX_NT_PRSTATUS:
345 		return (linux_ptrace_getregset_prstatus(td, pid, data));
346 	case LINUX_NT_PRFPREG:
347 		linux_msg(td, "PTRAGE_GETREGSET NT_PRFPREG not implemented; "
348 		    "returning EINVAL");
349 		return (EINVAL);
350 	case LINUX_NT_X86_XSTATE:
351 		linux_msg(td, "PTRAGE_GETREGSET NT_X86_XSTATE not implemented; "
352 		    "returning EINVAL");
353 		return (EINVAL);
354 	default:
355 		linux_msg(td, "PTRACE_GETREGSET request %#lx not implemented; "
356 		    "returning EINVAL", addr);
357 		return (EINVAL);
358 	}
359 }
360 
361 static int
362 linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data)
363 {
364 
365 	linux_msg(td, "PTRACE_SEIZE not implemented; returning EINVAL");
366 	return (EINVAL);
367 }
368 
369 static int
370 linux_ptrace_get_syscall_info(struct thread *td, pid_t pid,
371     l_ulong len, l_ulong data)
372 {
373 	struct ptrace_lwpinfo lwpinfo;
374 	struct ptrace_sc_ret sr;
375 	struct reg b_reg;
376 	struct syscall_info si;
377 	int error;
378 
379 	error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo));
380 	if (error != 0) {
381 		linux_msg(td, "PT_LWPINFO failed with error %d", error);
382 		return (error);
383 	}
384 
385 	memset(&si, 0, sizeof(si));
386 
387 	if (lwpinfo.pl_flags & PL_FLAG_SCE) {
388 		si.op = LINUX_PTRACE_SYSCALL_INFO_ENTRY;
389 		si.entry.nr = lwpinfo.pl_syscall_code;
390 		/*
391 		 * The use of PT_GET_SC_ARGS there is special,
392 		 * implementation of PT_GET_SC_ARGS for Linux-ABI
393 		 * callers emulates Linux bug which strace(1) depends
394 		 * on: at initialization it tests whether ptrace works
395 		 * by calling close(2), or some other single-argument
396 		 * syscall, _with six arguments_, and then verifies
397 		 * whether it can fetch them all using this API;
398 		 * otherwise it bails out.
399 		 */
400 		error = kern_ptrace(td, PT_GET_SC_ARGS, pid,
401 		    &si.entry.args, sizeof(si.entry.args));
402 		if (error != 0) {
403 			linux_msg(td, "PT_GET_SC_ARGS failed with error %d",
404 			    error);
405 			return (error);
406 		}
407 	} else if (lwpinfo.pl_flags & PL_FLAG_SCX) {
408 		si.op = LINUX_PTRACE_SYSCALL_INFO_EXIT;
409 		error = kern_ptrace(td, PT_GET_SC_RET, pid, &sr, sizeof(sr));
410 
411 		if (error != 0) {
412 			linux_msg(td, "PT_GET_SC_RET failed with error %d",
413 			    error);
414 			return (error);
415 		}
416 
417 		if (sr.sr_error == 0) {
418 			si.exit.rval = sr.sr_retval[0];
419 			si.exit.is_error = 0;
420 		} else if (sr.sr_error == EJUSTRETURN) {
421 			/*
422 			 * EJUSTRETURN means the actual value to return
423 			 * has already been put into td_frame; instead
424 			 * of extracting it and trying to determine whether
425 			 * it's an error or not just bail out and let
426 			 * the ptracing process fall back to another method.
427 			 */
428 			si.op = LINUX_PTRACE_SYSCALL_INFO_NONE;
429 		} else if (sr.sr_error == ERESTART) {
430 			si.exit.rval = -LINUX_ERESTARTSYS;
431 			si.exit.is_error = 1;
432 		} else {
433 			si.exit.rval = bsd_to_linux_errno(sr.sr_error);
434 			si.exit.is_error = 1;
435 		}
436 	} else {
437 		si.op = LINUX_PTRACE_SYSCALL_INFO_NONE;
438 	}
439 
440 	error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0);
441 	if (error != 0)
442 		return (error);
443 
444 	linux_ptrace_get_syscall_info_machdep(&b_reg, &si);
445 
446 	len = MIN(len, sizeof(si));
447 	error = copyout(&si, (void *)data, len);
448 	if (error == 0)
449 		td->td_retval[0] = sizeof(si);
450 
451 	return (error);
452 }
453 
454 int
455 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
456 {
457 	void *addr;
458 	pid_t pid;
459 	int error, sig;
460 
461 	if (!allow_ptrace)
462 		return (ENOSYS);
463 
464 	pid  = (pid_t)uap->pid;
465 	addr = (void *)uap->addr;
466 
467 	switch (uap->req) {
468 	case LINUX_PTRACE_TRACEME:
469 		error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0);
470 		break;
471 	case LINUX_PTRACE_PEEKTEXT:
472 	case LINUX_PTRACE_PEEKDATA:
473 		error = linux_ptrace_peek(td, pid, addr, (void *)uap->data);
474 		if (error != 0)
475 			goto out;
476 		/*
477 		 * Linux expects this syscall to read 64 bits, not 32.
478 		 */
479 		error = linux_ptrace_peek(td, pid,
480 		    (void *)(uap->addr + 4), (void *)(uap->data + 4));
481 		break;
482 	case LINUX_PTRACE_PEEKUSER:
483 		error = linux_ptrace_peekuser(td, pid, addr, (void *)uap->data);
484 		break;
485 	case LINUX_PTRACE_POKETEXT:
486 	case LINUX_PTRACE_POKEDATA:
487 		error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data);
488 		if (error != 0)
489 			goto out;
490 		/*
491 		 * Linux expects this syscall to write 64 bits, not 32.
492 		 */
493 		error = kern_ptrace(td, PT_WRITE_D, pid,
494 		    (void *)(uap->addr + 4), uap->data >> 32);
495 		break;
496 	case LINUX_PTRACE_POKEUSER:
497 		error = linux_ptrace_pokeuser(td, pid, addr, (void *)uap->data);
498 		break;
499 	case LINUX_PTRACE_CONT:
500 		error = map_signum(uap->data, &sig);
501 		if (error != 0)
502 			break;
503 		error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig);
504 		break;
505 	case LINUX_PTRACE_KILL:
506 		error = kern_ptrace(td, PT_KILL, pid, addr, uap->data);
507 		break;
508 	case LINUX_PTRACE_SINGLESTEP:
509 		error = map_signum(uap->data, &sig);
510 		if (error != 0)
511 			break;
512 		error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig);
513 		break;
514 	case LINUX_PTRACE_GETREGS:
515 		error = linux_ptrace_getregs(td, pid, (void *)uap->data);
516 		break;
517 	case LINUX_PTRACE_SETREGS:
518 		error = linux_ptrace_setregs(td, pid, (void *)uap->data);
519 		break;
520 	case LINUX_PTRACE_ATTACH:
521 		error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);
522 		break;
523 	case LINUX_PTRACE_DETACH:
524 		error = map_signum(uap->data, &sig);
525 		if (error != 0)
526 			break;
527 		error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig);
528 		break;
529 	case LINUX_PTRACE_SYSCALL:
530 		error = map_signum(uap->data, &sig);
531 		if (error != 0)
532 			break;
533 		error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig);
534 		break;
535 	case LINUX_PTRACE_SETOPTIONS:
536 		error = linux_ptrace_setoptions(td, pid, uap->data);
537 		break;
538 	case LINUX_PTRACE_GETEVENTMSG:
539 		error = linux_ptrace_geteventmsg(td, pid, uap->data);
540 		break;
541 	case LINUX_PTRACE_GETSIGINFO:
542 		error = linux_ptrace_getsiginfo(td, pid, uap->data);
543 		break;
544 	case LINUX_PTRACE_GETREGSET:
545 		error = linux_ptrace_getregset(td, pid, uap->addr, uap->data);
546 		break;
547 	case LINUX_PTRACE_SEIZE:
548 		error = linux_ptrace_seize(td, pid, uap->addr, uap->data);
549 		break;
550 	case LINUX_PTRACE_GET_SYSCALL_INFO:
551 		error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data);
552 		break;
553 	default:
554 		linux_msg(td, "ptrace(%ld, ...) not implemented; "
555 		    "returning EINVAL", uap->req);
556 		error = EINVAL;
557 		break;
558 	}
559 
560 out:
561 	if (error == EBUSY)
562 		error = ESRCH;
563 
564 	return (error);
565 }
566