1 | /* $NetBSD: kern_core.c,v 1.24 2016/07/07 06:55:43 msaitoh Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1982, 1986, 1989, 1991, 1993 |
5 | * The Regents of the University of California. All rights reserved. |
6 | * (c) UNIX System Laboratories, Inc. |
7 | * All or some portions of this file are derived from material licensed |
8 | * to the University of California by American Telephone and Telegraph |
9 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with |
10 | * the permission of UNIX System Laboratories, Inc. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * 3. Neither the name of the University nor the names of its contributors |
21 | * may be used to endorse or promote products derived from this software |
22 | * without specific prior written permission. |
23 | * |
24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
34 | * SUCH DAMAGE. |
35 | * |
36 | * @(#)kern_sig.c 8.14 (Berkeley) 5/14/95 |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: kern_core.c,v 1.24 2016/07/07 06:55:43 msaitoh Exp $" ); |
41 | |
42 | #include <sys/param.h> |
43 | #include <sys/vnode.h> |
44 | #include <sys/namei.h> |
45 | #include <sys/acct.h> |
46 | #include <sys/file.h> |
47 | #include <sys/stat.h> |
48 | #include <sys/proc.h> |
49 | #include <sys/exec.h> |
50 | #include <sys/filedesc.h> |
51 | #include <sys/kauth.h> |
52 | #include <sys/module.h> |
53 | |
54 | MODULE(MODULE_CLASS_MISC, coredump, NULL); |
55 | |
56 | struct coredump_iostate { |
57 | struct lwp *io_lwp; |
58 | struct vnode *io_vp; |
59 | kauth_cred_t io_cred; |
60 | off_t io_offset; |
61 | }; |
62 | |
63 | static int coredump(struct lwp *, const char *); |
64 | static int coredump_buildname(struct proc *, char *, const char *, size_t); |
65 | |
66 | static int |
67 | coredump_modcmd(modcmd_t cmd, void *arg) |
68 | { |
69 | |
70 | switch (cmd) { |
71 | case MODULE_CMD_INIT: |
72 | coredump_vec = coredump; |
73 | return 0; |
74 | case MODULE_CMD_FINI: |
75 | /* |
76 | * In theory we don't need to patch this, as the various |
77 | * exec formats depend on this module. If this module has |
78 | * no references, and so can be unloaded, no user programs |
79 | * can be running and so nothing can call *coredump_vec. |
80 | */ |
81 | coredump_vec = (int (*)(struct lwp *, const char *))enosys; |
82 | return 0; |
83 | default: |
84 | return ENOTTY; |
85 | } |
86 | } |
87 | |
88 | /* |
89 | * Dump core, into a file named "progname.core" or "core" (depending on the |
90 | * value of shortcorename), unless the process was setuid/setgid. |
91 | */ |
92 | static int |
93 | coredump(struct lwp *l, const char *pattern) |
94 | { |
95 | struct vnode *vp; |
96 | struct proc *p; |
97 | struct vmspace *vm; |
98 | kauth_cred_t cred; |
99 | struct pathbuf *pb; |
100 | struct nameidata nd; |
101 | struct vattr vattr; |
102 | struct coredump_iostate io; |
103 | struct plimit *lim; |
104 | int error, error1; |
105 | char *name, *lastslash; |
106 | |
107 | name = PNBUF_GET(); |
108 | |
109 | p = l->l_proc; |
110 | vm = p->p_vmspace; |
111 | |
112 | mutex_enter(proc_lock); /* p_session */ |
113 | mutex_enter(p->p_lock); |
114 | |
115 | /* |
116 | * Refuse to core if the data + stack + user size is larger than |
117 | * the core dump limit. XXX THIS IS WRONG, because of mapped |
118 | * data. |
119 | */ |
120 | if (USPACE + ctob(vm->vm_dsize + vm->vm_ssize) >= |
121 | p->p_rlimit[RLIMIT_CORE].rlim_cur) { |
122 | error = EFBIG; /* better error code? */ |
123 | mutex_exit(p->p_lock); |
124 | mutex_exit(proc_lock); |
125 | goto done; |
126 | } |
127 | |
128 | /* |
129 | * It may well not be curproc, so grab a reference to its current |
130 | * credentials. |
131 | */ |
132 | kauth_cred_hold(p->p_cred); |
133 | cred = p->p_cred; |
134 | |
135 | /* |
136 | * Make sure the process has not set-id, to prevent data leaks, |
137 | * unless it was specifically requested to allow set-id coredumps. |
138 | */ |
139 | if (p->p_flag & PK_SUGID) { |
140 | if (!security_setidcore_dump) { |
141 | error = EPERM; |
142 | mutex_exit(p->p_lock); |
143 | mutex_exit(proc_lock); |
144 | goto done; |
145 | } |
146 | pattern = security_setidcore_path; |
147 | } |
148 | |
149 | /* Lock, as p_limit and pl_corename might change. */ |
150 | lim = p->p_limit; |
151 | mutex_enter(&lim->pl_lock); |
152 | if (pattern == NULL) { |
153 | pattern = lim->pl_corename; |
154 | } |
155 | error = coredump_buildname(p, name, pattern, MAXPATHLEN); |
156 | mutex_exit(&lim->pl_lock); |
157 | |
158 | if (error) { |
159 | mutex_exit(p->p_lock); |
160 | mutex_exit(proc_lock); |
161 | goto done; |
162 | } |
163 | |
164 | /* |
165 | * On a simple filename, see if the filesystem allow us to write |
166 | * core dumps there. |
167 | */ |
168 | lastslash = strrchr(name, '/'); |
169 | if (!lastslash) { |
170 | vp = p->p_cwdi->cwdi_cdir; |
171 | if (vp->v_mount == NULL || |
172 | (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0) |
173 | error = EPERM; |
174 | } |
175 | |
176 | mutex_exit(p->p_lock); |
177 | mutex_exit(proc_lock); |
178 | if (error) |
179 | goto done; |
180 | |
181 | /* |
182 | * On a complex filename, see if the filesystem allow us to write |
183 | * core dumps there. |
184 | * |
185 | * XXX: We should have an API that avoids double lookups |
186 | */ |
187 | if (lastslash) { |
188 | char c[2]; |
189 | |
190 | if (lastslash - name >= MAXPATHLEN - 2) { |
191 | error = EPERM; |
192 | goto done; |
193 | } |
194 | |
195 | c[0] = lastslash[1]; |
196 | c[1] = lastslash[2]; |
197 | lastslash[1] = '.'; |
198 | lastslash[2] = '\0'; |
199 | error = namei_simple_kernel(name, NSM_FOLLOW_NOEMULROOT, &vp); |
200 | if (error) |
201 | goto done; |
202 | if (vp->v_mount == NULL || |
203 | (vp->v_mount->mnt_flag & MNT_NOCOREDUMP) != 0) |
204 | error = EPERM; |
205 | vrele(vp); |
206 | if (error) |
207 | goto done; |
208 | lastslash[1] = c[0]; |
209 | lastslash[2] = c[1]; |
210 | } |
211 | |
212 | pb = pathbuf_create(name); |
213 | if (pb == NULL) { |
214 | error = ENOMEM; |
215 | goto done; |
216 | } |
217 | NDINIT(&nd, LOOKUP, NOFOLLOW, pb); |
218 | if ((error = vn_open(&nd, O_CREAT | O_NOFOLLOW | FWRITE, |
219 | S_IRUSR | S_IWUSR)) != 0) { |
220 | pathbuf_destroy(pb); |
221 | goto done; |
222 | } |
223 | vp = nd.ni_vp; |
224 | pathbuf_destroy(pb); |
225 | |
226 | /* |
227 | * Don't dump to: |
228 | * - non-regular files |
229 | * - files with links |
230 | * - files we don't own |
231 | */ |
232 | if (vp->v_type != VREG || |
233 | VOP_GETATTR(vp, &vattr, cred) || vattr.va_nlink != 1 || |
234 | vattr.va_uid != kauth_cred_geteuid(cred)) { |
235 | error = EACCES; |
236 | goto out; |
237 | } |
238 | vattr_null(&vattr); |
239 | vattr.va_size = 0; |
240 | |
241 | if ((p->p_flag & PK_SUGID) && security_setidcore_dump) { |
242 | vattr.va_uid = security_setidcore_owner; |
243 | vattr.va_gid = security_setidcore_group; |
244 | vattr.va_mode = security_setidcore_mode; |
245 | } |
246 | |
247 | VOP_SETATTR(vp, &vattr, cred); |
248 | p->p_acflag |= ACORE; |
249 | |
250 | io.io_lwp = l; |
251 | io.io_vp = vp; |
252 | io.io_cred = cred; |
253 | io.io_offset = 0; |
254 | |
255 | /* Now dump the actual core file. */ |
256 | error = (*p->p_execsw->es_coredump)(l, &io); |
257 | out: |
258 | VOP_UNLOCK(vp); |
259 | error1 = vn_close(vp, FWRITE, cred); |
260 | if (error == 0) |
261 | error = error1; |
262 | done: |
263 | if (name != NULL) |
264 | PNBUF_PUT(name); |
265 | return error; |
266 | } |
267 | |
268 | static int |
269 | coredump_buildname(struct proc *p, char *dst, const char *src, size_t len) |
270 | { |
271 | const char *s; |
272 | char *d, *end; |
273 | int i; |
274 | |
275 | KASSERT(mutex_owned(proc_lock)); |
276 | |
277 | for (s = src, d = dst, end = d + len; *s != '\0'; s++) { |
278 | if (*s == '%') { |
279 | switch (*(s + 1)) { |
280 | case 'n': |
281 | i = snprintf(d, end - d, "%s" , p->p_comm); |
282 | break; |
283 | case 'p': |
284 | i = snprintf(d, end - d, "%d" , p->p_pid); |
285 | break; |
286 | case 'u': |
287 | i = snprintf(d, end - d, "%.*s" , |
288 | (int)sizeof p->p_pgrp->pg_session->s_login, |
289 | p->p_pgrp->pg_session->s_login); |
290 | break; |
291 | case 't': |
292 | i = snprintf(d, end - d, "%lld" , |
293 | (long long)p->p_stats->p_start.tv_sec); |
294 | break; |
295 | default: |
296 | goto copy; |
297 | } |
298 | d += i; |
299 | s++; |
300 | } else { |
301 | copy: *d = *s; |
302 | d++; |
303 | } |
304 | if (d >= end) |
305 | return (ENAMETOOLONG); |
306 | } |
307 | *d = '\0'; |
308 | return 0; |
309 | } |
310 | |
311 | int |
312 | coredump_write(struct coredump_iostate *io, enum uio_seg segflg, |
313 | const void *data, size_t len) |
314 | { |
315 | int error; |
316 | |
317 | error = vn_rdwr(UIO_WRITE, io->io_vp, __UNCONST(data), len, |
318 | io->io_offset, segflg, |
319 | IO_NODELOCKED|IO_UNIT, io->io_cred, NULL, |
320 | segflg == UIO_USERSPACE ? io->io_lwp : NULL); |
321 | if (error) { |
322 | printf("pid %d (%s): %s write of %zu@%p at %lld failed: %d\n" , |
323 | io->io_lwp->l_proc->p_pid, io->io_lwp->l_proc->p_comm, |
324 | segflg == UIO_USERSPACE ? "user" : "system" , |
325 | len, data, (long long) io->io_offset, error); |
326 | return (error); |
327 | } |
328 | |
329 | io->io_offset += len; |
330 | return (0); |
331 | } |
332 | |
333 | off_t |
334 | coredump_offset(struct coredump_iostate *io) |
335 | { |
336 | return io->io_offset; |
337 | } |
338 | |