1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009 Robert N. M. Watson
5 * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31
32 #define _WANT_PRISON
33 #define _WANT_UCRED
34 #define _WANT_VNET
35
36 #include <sys/_lock.h>
37 #include <sys/_mutex.h>
38 #include <sys/_task.h>
39 #include <sys/jail.h>
40 #include <sys/proc.h>
41 #include <sys/types.h>
42
43 #include <stdbool.h>
44 #include <net/vnet.h>
45
46 #include <kvm.h>
47 #include <limits.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50
51 #include "kvm_private.h"
52
53 /*
54 * Set up libkvm to handle virtual network stack symbols by selecting a
55 * starting pid.
56 */
57 int
_kvm_vnet_selectpid(kvm_t * kd,pid_t pid)58 _kvm_vnet_selectpid(kvm_t *kd, pid_t pid)
59 {
60 struct proc proc;
61 struct ucred cred;
62 struct prison prison;
63 struct vnet vnet;
64 struct kvm_nlist nl[] = {
65 /*
66 * Note: kvm_nlist strips the first '_' so add an extra one
67 * here to __{start,stop}_set_vnet.
68 */
69 #define NLIST_START_VNET 0
70 { .n_name = "___start_" VNET_SETNAME },
71 #define NLIST_STOP_VNET 1
72 { .n_name = "___stop_" VNET_SETNAME },
73 #define NLIST_VNET_HEAD 2
74 { .n_name = "vnet_head" },
75 #define NLIST_ALLPROC 3
76 { .n_name = "allproc" },
77 #define NLIST_DUMPTID 4
78 { .n_name = "dumptid" },
79 #define NLIST_PROC0 5
80 { .n_name = "proc0" },
81 { .n_name = NULL },
82 };
83 uintptr_t procp, credp;
84 #define VMCORE_VNET_OF_PROC0
85 #ifndef VMCORE_VNET_OF_PROC0
86 struct thread td;
87 uintptr_t tdp;
88 #endif
89 lwpid_t dumptid;
90
91 /*
92 * XXX: This only works for native kernels for now.
93 */
94 if (!kvm_native(kd))
95 return (-1);
96
97 /*
98 * Locate and cache locations of important symbols
99 * using the internal version of _kvm_nlist, turning
100 * off initialization to avoid recursion in case of
101 * unresolveable symbols.
102 */
103 if (_kvm_nlist(kd, nl, 0) != 0) {
104 /*
105 * XXX-BZ: ___start_/___stop_VNET_SETNAME may fail.
106 * For now do not report an error here as we are called
107 * internally and in `void context' until we merge the
108 * functionality to optionally activate this into programs.
109 * By that time we can properly fail and let the callers
110 * handle the error.
111 */
112 /* _kvm_err(kd, kd->program, "%s: no namelist", __func__); */
113 return (-1);
114 }
115
116 /*
117 * Auto-detect if this is a crashdump by reading dumptid.
118 */
119 dumptid = 0;
120 if (nl[NLIST_DUMPTID].n_value) {
121 if (kvm_read(kd, nl[NLIST_DUMPTID].n_value, &dumptid,
122 sizeof(dumptid)) != sizeof(dumptid)) {
123 _kvm_err(kd, kd->program, "%s: dumptid", __func__);
124 return (-1);
125 }
126 }
127
128 /*
129 * First, find the process for this pid. If we are working on a
130 * dump, either locate the thread dumptid is referring to or proc0.
131 * Based on either, take the address of the ucred.
132 */
133 credp = 0;
134
135 procp = nl[NLIST_ALLPROC].n_value;
136 #ifdef VMCORE_VNET_OF_PROC0
137 if (dumptid > 0) {
138 procp = nl[NLIST_PROC0].n_value;
139 pid = 0;
140 }
141 #endif
142 while (procp != 0) {
143 if (kvm_read(kd, procp, &proc, sizeof(proc)) != sizeof(proc)) {
144 _kvm_err(kd, kd->program, "%s: proc", __func__);
145 return (-1);
146 }
147 #ifndef VMCORE_VNET_OF_PROC0
148 if (dumptid > 0) {
149 tdp = (uintptr_t)TAILQ_FIRST(&proc.p_threads);
150 while (tdp != 0) {
151 if (kvm_read(kd, tdp, &td, sizeof(td)) !=
152 sizeof(td)) {
153 _kvm_err(kd, kd->program, "%s: thread",
154 __func__);
155 return (-1);
156 }
157 if (td.td_tid == dumptid) {
158 credp = (uintptr_t)td.td_ucred;
159 break;
160 }
161 tdp = (uintptr_t)TAILQ_NEXT(&td, td_plist);
162 }
163 } else
164 #endif
165 if (proc.p_pid == pid)
166 credp = (uintptr_t)proc.p_ucred;
167 if (credp != 0)
168 break;
169 procp = (uintptr_t)LIST_NEXT(&proc, p_list);
170 }
171 if (credp == 0) {
172 _kvm_err(kd, kd->program, "%s: pid/tid not found", __func__);
173 return (-1);
174 }
175 if (kvm_read(kd, (uintptr_t)credp, &cred, sizeof(cred)) !=
176 sizeof(cred)) {
177 _kvm_err(kd, kd->program, "%s: cred", __func__);
178 return (-1);
179 }
180 if (cred.cr_prison == NULL) {
181 _kvm_err(kd, kd->program, "%s: no jail", __func__);
182 return (-1);
183 }
184 if (kvm_read(kd, (uintptr_t)cred.cr_prison, &prison, sizeof(prison)) !=
185 sizeof(prison)) {
186 _kvm_err(kd, kd->program, "%s: prison", __func__);
187 return (-1);
188 }
189 if (prison.pr_vnet == NULL) {
190 _kvm_err(kd, kd->program, "%s: no vnet", __func__);
191 return (-1);
192 }
193 if (kvm_read(kd, (uintptr_t)prison.pr_vnet, &vnet, sizeof(vnet)) !=
194 sizeof(vnet)) {
195 _kvm_err(kd, kd->program, "%s: vnet", __func__);
196 return (-1);
197 }
198 if (vnet.vnet_magic_n != VNET_MAGIC_N) {
199 _kvm_err(kd, kd->program, "%s: invalid vnet magic#", __func__);
200 return (-1);
201 }
202 kd->vnet_initialized = 1;
203 kd->vnet_start = nl[NLIST_START_VNET].n_value;
204 kd->vnet_stop = nl[NLIST_STOP_VNET].n_value;
205 kd->vnet_current = (uintptr_t)prison.pr_vnet;
206 kd->vnet_base = vnet.vnet_data_base;
207 return (0);
208 }
209
210 /*
211 * Check whether the vnet module has been initialized successfully
212 * or not, initialize it if permitted.
213 */
214 int
_kvm_vnet_initialized(kvm_t * kd,int intialize)215 _kvm_vnet_initialized(kvm_t *kd, int intialize)
216 {
217
218 if (kd->vnet_initialized || !intialize)
219 return (kd->vnet_initialized);
220
221 (void) _kvm_vnet_selectpid(kd, getpid());
222
223 return (kd->vnet_initialized);
224 }
225
226 /*
227 * Check whether the value is within the vnet symbol range and
228 * only if so adjust the offset relative to the current base.
229 */
230 kvaddr_t
_kvm_vnet_validaddr(kvm_t * kd,kvaddr_t value)231 _kvm_vnet_validaddr(kvm_t *kd, kvaddr_t value)
232 {
233
234 if (value == 0)
235 return (value);
236
237 if (!kd->vnet_initialized)
238 return (value);
239
240 if (value < kd->vnet_start || value >= kd->vnet_stop)
241 return (value);
242
243 return (kd->vnet_base + value);
244 }
245